|
@@ -4,34 +4,55 @@ import cfca.paperless.ClientConstants;
|
|
|
import cfca.paperless.base.BaseConstants;
|
|
|
import cfca.paperless.base.util.Base64;
|
|
|
import cfca.paperless.base.util.GUIDUtil;
|
|
|
+import cfca.paperless.base.util.ImageUtil;
|
|
|
import cfca.paperless.base.util.PwdEncryptUtil;
|
|
|
import cfca.paperless.client.PaperlessClient;
|
|
|
import cfca.paperless.dto.RequestHead;
|
|
|
import cfca.paperless.dto.ResponseDto;
|
|
|
import cfca.paperless.dto.ResponseHead;
|
|
|
-import cfca.paperless.dto.bean.SealCertBean;
|
|
|
-import cfca.paperless.dto.bean.SealInfoBean;
|
|
|
+import cfca.paperless.dto.bean.*;
|
|
|
import cfca.paperless.dto.request.requestbody.tx20.MakeSealRequestBody;
|
|
|
+import cfca.paperless.dto.request.requestbody.tx40.CompoundSealPdfListRequestBody;
|
|
|
import cfca.paperless.dto.request.tx20.MakeSealRequest;
|
|
|
import cfca.paperless.dto.request.tx40.CompoundSealPdfListDetachedRequest;
|
|
|
import cfca.paperless.dto.response.responsebody.tx20.MakeSealResponseBody;
|
|
|
+import cfca.paperless.dto.response.responsebody.tx40.CompoundSealPdfListDetachedResponseBody;
|
|
|
import cfca.paperless.dto.response.tx20.MakeSealResponse;
|
|
|
+import cfca.paperless.dto.response.tx40.CompoundSealPdfListDetachedResponse;
|
|
|
import com.alibaba.fastjson.JSONObject;
|
|
|
import lombok.AllArgsConstructor;
|
|
|
+import org.apache.commons.lang.StringUtils;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
import org.springblade.business.feign.TaskClient;
|
|
|
import org.springblade.business.vo.TaskApprovalVO;
|
|
|
import org.springblade.common.constant.EVisaConstant;
|
|
|
import org.springblade.common.utils.CommonUtil;
|
|
|
+import org.springblade.core.oss.model.BladeFile;
|
|
|
import org.springblade.core.secure.utils.AuthUtil;
|
|
|
import org.springblade.evisa.redissionUtil.DistributedRedisLock;
|
|
|
import org.springblade.evisa.service.EVisaService;
|
|
|
import org.springblade.evisa.vo.EVisaMakeSealVO;
|
|
|
import org.springblade.evisa.vo.EVisaTaskApprovalVO;
|
|
|
+import org.springblade.evisa.vo.SealPdfVO;
|
|
|
+import org.springblade.evisa.vo.SealStrategyVO;
|
|
|
import org.springblade.manager.entity.SignPfxFile;
|
|
|
import org.springblade.manager.feign.SignPfxClient;
|
|
|
+import org.springblade.resource.feign.NewIOSSClient;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import javax.imageio.*;
|
|
|
+import javax.imageio.metadata.IIOInvalidTreeException;
|
|
|
+import javax.imageio.metadata.IIOMetadata;
|
|
|
+import javax.imageio.metadata.IIOMetadataNode;
|
|
|
+import javax.imageio.stream.ImageOutputStream;
|
|
|
+import java.awt.image.BufferedImage;
|
|
|
+import java.io.ByteArrayInputStream;
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
+import java.io.IOException;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Iterator;
|
|
|
import java.util.List;
|
|
|
|
|
|
@Service
|
|
@@ -42,12 +63,82 @@ public class EVisaServiceImpl implements EVisaService {
|
|
|
|
|
|
private static final String SIGN_PORT = "8183";
|
|
|
|
|
|
+ private static final double INCH_2_CM = 2.54d;
|
|
|
+
|
|
|
private static final Logger logger = LoggerFactory.getLogger(EVisaServiceImpl.class);
|
|
|
|
|
|
private final SignPfxClient signPfxClient;
|
|
|
|
|
|
private final TaskClient taskClient;
|
|
|
|
|
|
+ private final NewIOSSClient newIOSSClient;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String certification(String pdfUrl, String fileName, String contractId) {
|
|
|
+ try{
|
|
|
+ //准备签章策略
|
|
|
+ List<SealStrategyVO> sealStrategyVOS = new ArrayList<>();
|
|
|
+ //根据当前合同段获取相关的证书
|
|
|
+ List<SignPfxFile> pfxFiles = this.signPfxClient.querySignPfxByUserIdOrContractId("", contractId);
|
|
|
+ if(pfxFiles == null || pfxFiles.size() <= 0){
|
|
|
+ return "error";
|
|
|
+ }
|
|
|
+ SealStrategyVO vo = new SealStrategyVO();
|
|
|
+ vo.setSealCode(EVisaConstant.SIGN_SEAL_CODE + AuthUtil.getUserId().toString().substring(0, EVisaConstant.USER_ID_SUB));
|
|
|
+ vo.setSealPassword(AuthUtil.getUserId().toString().substring(0, EVisaConstant.USER_ID_SUB));
|
|
|
+ vo.setSealPerson(AuthUtil.getUserAccount());
|
|
|
+ //使用默认写死的透明图片
|
|
|
+ vo.setImageUrl("authentication");
|
|
|
+ //使用坐标定位 ,坐标固定
|
|
|
+ vo.setSealType("2");
|
|
|
+ vo.setLx("100");
|
|
|
+ vo.setLy("100");
|
|
|
+ vo.setPage("1");
|
|
|
+ sealStrategyVOS.add(vo);
|
|
|
+
|
|
|
+ SealPdfVO pdfVO = new SealPdfVO();
|
|
|
+ pdfVO.setStrategyVoList(sealStrategyVOS);
|
|
|
+
|
|
|
+ //获取认证文件
|
|
|
+ byte[] fileByte = CommonUtil.InputStreamToBytes(CommonUtil.getOSSInputStream(pdfUrl));
|
|
|
+ //兼容大文件签章
|
|
|
+ Object[] result;
|
|
|
+ //大于50M的单个文件采用大文件签章处理
|
|
|
+ if(fileByte.length > 52428800){
|
|
|
+ result = null;
|
|
|
+
|
|
|
+ } else {
|
|
|
+ //普通文件签章
|
|
|
+ result = this.signPdf(pdfVO, fileByte);
|
|
|
+ }
|
|
|
+ if(result != null){
|
|
|
+ if(result[0] != null){
|
|
|
+ byte[] newPdfData = (byte[])result[0];
|
|
|
+ //重新上传覆盖
|
|
|
+ BladeFile bladeFile = this.newIOSSClient.updateFile(newPdfData, pdfUrl.substring(pdfUrl.lastIndexOf("/") + 1, pdfUrl.lastIndexOf(".")));
|
|
|
+ if(bladeFile != null){
|
|
|
+ return "success" + "@@@@" + bladeFile.getLink();
|
|
|
+ } else {
|
|
|
+ return "error";
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ String s = result[1].toString();
|
|
|
+ return s.contains("600619") ? "认证失败! " + fileName + "-文件存在无效签名。" : "认证失败!"+"原因:" + s;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ //电签失败 ,接口返回认证失败
|
|
|
+ return "认证失败!认证接口调用异常";
|
|
|
+ }
|
|
|
+
|
|
|
+ }catch (Exception e){
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
@Override
|
|
|
public String eVisa(EVisaTaskApprovalVO task) {
|
|
|
//todo 这里应当是配置限制参数,初版暂时写死
|
|
@@ -76,7 +167,235 @@ public class EVisaServiceImpl implements EVisaService {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 签章
|
|
|
+ * 签章(坐标方式)
|
|
|
+ */
|
|
|
+ private Object[] signPdf(SealPdfVO pdfVO, byte[] fileByte){
|
|
|
+ Object[] result = new Object[2];
|
|
|
+ try{
|
|
|
+ PaperlessClient paperlessClient = new PaperlessClient(SIGN_HOST, SIGN_PORT, 300000, 36000000);
|
|
|
+ paperlessClient.setSSL(false);
|
|
|
+ //*****************************************************************************
|
|
|
+ CompoundSealPdfListDetachedRequest compoundSealPdfListDetachedRequest = new CompoundSealPdfListDetachedRequest();
|
|
|
+
|
|
|
+ RequestHead requestHead = new RequestHead();
|
|
|
+ //业务流水号 非空
|
|
|
+ String transactionNo = GUIDUtil.generateId();
|
|
|
+ //机构编码非空
|
|
|
+ String organizationCode = EVisaConstant.organizationCode;
|
|
|
+
|
|
|
+ //操作员编码 可为空(企业类型不能为空)
|
|
|
+ String operatorCode = EVisaConstant.operationCode;
|
|
|
+ //渠道编码 可为空
|
|
|
+ String channelCode = "";
|
|
|
+ //设置属性
|
|
|
+ requestHead.setBasicInfo(transactionNo, organizationCode,operatorCode,channelCode);
|
|
|
+
|
|
|
+ compoundSealPdfListDetachedRequest.setHead(requestHead);
|
|
|
+
|
|
|
+ //*****************************************************************************
|
|
|
+ CompoundSealPdfListRequestBody requestBody = new CompoundSealPdfListRequestBody();
|
|
|
+
|
|
|
+ List<PdfBean> pdfBeans = new ArrayList<>();
|
|
|
+ PdfBean pdfBean = new PdfBean();
|
|
|
+ pdfBean.setBizSerialNo(GUIDUtil.generateId());
|
|
|
+ pdfBean.setInputSource("");
|
|
|
+ pdfBean.setInputType(BaseConstants.INPUT_TYPE_FILEDATA);
|
|
|
+ pdfBean.setPdfData(fileByte);
|
|
|
+ pdfBeans.add(pdfBean);
|
|
|
+
|
|
|
+ requestBody.setPdfBeans(pdfBeans);
|
|
|
+ //***********************构造机构章策略 ********************************
|
|
|
+ List<SealStrategy> sealStrategies = this.generateSealStrategies(pdfVO.getStrategyVoList());
|
|
|
+ if(null == sealStrategies || sealStrategies.size() <= 0){
|
|
|
+ logger.info("【电签模块】{}","签章策略为空");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ requestBody.setSealStrategies(sealStrategies);
|
|
|
+
|
|
|
+ //签章后文件保存地址,不为空时,直接将签章文件保存在此地址,不再返回签章后文档数据;ftp:auto
|
|
|
+ requestBody.setOutputFilepath("");
|
|
|
+
|
|
|
+ //时间戳方式,默认为0;0:实时访问CFCA 时间戳服务;1:使用从CFCA购置并在本地部署的时间戳服务器产品;
|
|
|
+ requestBody.setTimestampChannel(BaseConstants.TIME_STAMP_CHANNEL_CFCA);
|
|
|
+ //获取场景证书的方式默认值为0;0:实时从CFCA CA服务申请下载场景证书;1:使用从CFCA CA服务预先申请下载并存储在本地的场景证书;
|
|
|
+ requestBody.setSceneCertChannel(BaseConstants.SCEND_CERT_CHANNEL_REAL);
|
|
|
+
|
|
|
+ compoundSealPdfListDetachedRequest.setBody(requestBody);
|
|
|
+ //****************************** 请求服务端进行签章 *********************************************
|
|
|
+ ResponseDto responseDto = paperlessClient.execute(compoundSealPdfListDetachedRequest);
|
|
|
+
|
|
|
+ //******************************解析响应结果 *********************************************
|
|
|
+ CompoundSealPdfListDetachedResponse compoundSealPdfListDetachedResponse = (CompoundSealPdfListDetachedResponse)responseDto;
|
|
|
+ ResponseHead responseHead = compoundSealPdfListDetachedResponse.getHead();
|
|
|
+ CompoundSealPdfListDetachedResponseBody responseBody = compoundSealPdfListDetachedResponse.getBody();
|
|
|
+ if(ClientConstants.CODE_SUCCESS.equals(responseHead.getCode())){
|
|
|
+ List<PdfBean4Response> pdfBeanList = responseBody.getPdfBeans();
|
|
|
+ if(pdfBeanList!=null && pdfBeanList.size()>0){
|
|
|
+ PdfBean4Response pdfBean4Response =pdfBeanList.get(0);
|
|
|
+ result[0] = pdfBean4Response.getPdf();
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ logger.info("【电签模块】{}","签章响应Response:"+compoundSealPdfListDetachedResponse);
|
|
|
+ logger.info("【电签模块】{}","签章响应code:"+responseHead.getCode());
|
|
|
+ result[0] = null;
|
|
|
+ result[1] = compoundSealPdfListDetachedResponse.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (Exception e){
|
|
|
+ e.printStackTrace();
|
|
|
+ logger.info("【电签模块】{}","电签签章接口调用异常");
|
|
|
+ e.printStackTrace();
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构造机构章签章策略
|
|
|
+ * 使用 :1-印章绑定的图片
|
|
|
+ */
|
|
|
+ private List<SealStrategy> generateSealStrategies(List<SealStrategyVO> strategyVoList){
|
|
|
+ List<SealStrategy> sealStrategies = new ArrayList<>();
|
|
|
+
|
|
|
+ if(strategyVoList == null || strategyVoList.size() <= 0){
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ //构建策略
|
|
|
+ for(SealStrategyVO vo : strategyVoList){
|
|
|
+ try{
|
|
|
+ SealStrategy sealStrategy = new SealStrategy();
|
|
|
+
|
|
|
+ // 使用图片签章
|
|
|
+ String type = BaseConstants.SEAL_TYPE_IMAGE;
|
|
|
+
|
|
|
+ String sealCode = vo.getSealCode();
|
|
|
+ String sealPassword = PwdEncryptUtil.encrypto(vo.getSealPassword());
|
|
|
+ sealStrategy.setSealInfo(type, sealCode, sealPassword);
|
|
|
+
|
|
|
+ //单笔业务流水号 非空
|
|
|
+ String serialNo = GUIDUtil.generateId();
|
|
|
+ sealStrategy.setSerialNo(serialNo);
|
|
|
+
|
|
|
+ //算法
|
|
|
+ String hashAlg = BaseConstants.HASHALG_SHA256;
|
|
|
+ sealStrategy.setHashAlg(hashAlg);
|
|
|
+ //透明度,0-1.0f,默认1.0f,不透明
|
|
|
+ String fillOpacity = "1";
|
|
|
+ sealStrategy.setFillOpacity(fillOpacity);
|
|
|
+ //是否显示,默认1 显示
|
|
|
+ String visible = "1";
|
|
|
+ sealStrategy.setVisible(visible);
|
|
|
+
|
|
|
+ byte[] imageData = null;
|
|
|
+ if(!"authentication".equals(vo.getImageUrl())){
|
|
|
+ String imageUrl = vo.getImageUrl();
|
|
|
+ if(StringUtils.isEmpty(imageUrl)){
|
|
|
+ logger.info("签章图片url为null,签章人员:" + vo.getSealPerson());
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ //获取字节
|
|
|
+ imageData = CommonUtil.InputStreamToBytes(CommonUtil.getOSSInputStream(imageUrl));
|
|
|
+ //对图片进行扣白底
|
|
|
+ imageData = ImageUtil.transferAlpha(imageData);
|
|
|
+
|
|
|
+ //设置大小
|
|
|
+ if(vo.isCompanySeal()){
|
|
|
+ imageData = ImageUtil.resizeImage(imageData,540,540);
|
|
|
+ }else {
|
|
|
+ imageData = ImageUtil.resizeImage(imageData,480,132);
|
|
|
+ }
|
|
|
+
|
|
|
+ //图片dpi设置
|
|
|
+ imageData = this.handleDpi(imageData);
|
|
|
+ }
|
|
|
+ if (imageData != null) {
|
|
|
+ //图片进行Base64编码/
|
|
|
+ String imageDataString = new String(Base64.encode(imageData), StandardCharsets.UTF_8);
|
|
|
+ sealStrategy.setSealImage(imageDataString);
|
|
|
+ }
|
|
|
+
|
|
|
+ if("authentication".equals(vo.getImageUrl())){
|
|
|
+ //工程文件认证的 是要用透明图片
|
|
|
+ sealStrategy.setSealImage(EVisaConstant.base64String);
|
|
|
+ }
|
|
|
+ sealStrategy.setSealMiscInfo(vo.getSealPerson(), vo.getSealLocation(), vo.getSealReason());
|
|
|
+
|
|
|
+ //关键字签章
|
|
|
+ sealStrategy.setSealType(vo.getSealType());
|
|
|
+ if (vo.getSealType().equals("3")) {
|
|
|
+ sealStrategy.setKeyword(vo.getKeyword());
|
|
|
+ sealStrategy.setOffsetX(vo.getOffSetX());
|
|
|
+ sealStrategy.setOffsetY(vo.getOffSetY());
|
|
|
+ } else if (vo.getSealType().equals("2")) {
|
|
|
+ //设置PDF坐标原点,签章图片定位点 默认为PDF左下角,签章图片定位为左下角
|
|
|
+ if(StringUtils.isNotEmpty(vo.getIsCenterCoordinate())){
|
|
|
+ sealStrategy.setIsCenterCoordinate(vo.getIsCenterCoordinate());
|
|
|
+ }
|
|
|
+ sealStrategy.setSignWithCoordinate(vo.getPage(), vo.getLx(), vo.getLy());
|
|
|
+ }
|
|
|
+
|
|
|
+ sealStrategies.add(sealStrategy);
|
|
|
+ }catch (Exception e){
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return sealStrategies;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置图片的dpi
|
|
|
+ */
|
|
|
+ private byte[] handleDpi(byte[] bytes) throws IOException {
|
|
|
+ BufferedImage gridImage = ImageIO.read(new ByteArrayInputStream(bytes));
|
|
|
+ final String formatName = "png";
|
|
|
+ byte[] result = new byte[0];
|
|
|
+ for (Iterator<ImageWriter> iw = ImageIO.getImageWritersByFormatName(formatName); iw.hasNext();) {
|
|
|
+ ImageWriter writer = iw.next();
|
|
|
+ ImageWriteParam writeParam = writer.getDefaultWriteParam();
|
|
|
+ ImageTypeSpecifier typeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(BufferedImage.TYPE_INT_RGB);
|
|
|
+ IIOMetadata metadata = writer.getDefaultImageMetadata(typeSpecifier, writeParam);
|
|
|
+ if (metadata.isReadOnly() || !metadata.isStandardMetadataFormatSupported()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ //设置图片的dpi
|
|
|
+ this.setDPI(metadata);
|
|
|
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
|
|
|
+ try (ImageOutputStream stream = ImageIO.createImageOutputStream(output)) {
|
|
|
+ writer.setOutput(stream);
|
|
|
+ writer.write(metadata, new IIOImage(gridImage, null, metadata), writeParam);
|
|
|
+ result = output.toByteArray();
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置图片的dpi
|
|
|
+ */
|
|
|
+ private void setDPI(IIOMetadata metadata) throws IIOInvalidTreeException {
|
|
|
+ // for PMG, it's dots per millimeter
|
|
|
+ double dotsPerMilli = 1.0 * 300 / 10 / INCH_2_CM;
|
|
|
+ IIOMetadataNode horiz = new IIOMetadataNode("HorizontalPixelSize");
|
|
|
+ horiz.setAttribute("value", Double.toString(dotsPerMilli));
|
|
|
+
|
|
|
+ IIOMetadataNode vert = new IIOMetadataNode("VerticalPixelSize");
|
|
|
+ vert.setAttribute("value", Double.toString(dotsPerMilli));
|
|
|
+
|
|
|
+ IIOMetadataNode dim = new IIOMetadataNode("Dimension");
|
|
|
+ dim.appendChild(horiz);
|
|
|
+ dim.appendChild(vert);
|
|
|
+
|
|
|
+ IIOMetadataNode root = new IIOMetadataNode("javax_imageio_1.0");
|
|
|
+ root.appendChild(dim);
|
|
|
+
|
|
|
+ metadata.mergeTree("javax_imageio_1.0", root);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 签章(线程方式)
|
|
|
*/
|
|
|
private boolean signPdf(){
|
|
|
StringBuffer stringbuffer = new StringBuffer();
|
|
@@ -110,6 +429,9 @@ public class EVisaServiceImpl implements EVisaService {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 创建印模
|
|
|
+ */
|
|
|
@Override
|
|
|
public String createSeal(EVisaMakeSealVO vo) {
|
|
|
try{
|