Java集成E签宝实现签署【全网最全】

完整代码:java-boot-highpin-background: 背调服务 (gitee.com)  【暂不开源】
前端代码:zhaopin: 招聘背调 (gitee.com) 【暂不开源】

全部代码在课程资料里面:Java实现集成E签宝签署_哔哩哔哩_bilibili

配置信息
1.在application.yml中配置appid、密钥信息,包含沙箱环境
    ```java
        esign:
            host: https://smlopenapi.esign.cn
            appId: your appId
            appSecret: your secret
    ```
步骤说明
2.实现电子签的主要流程在BaseAuthInfoServiceImpl里面
    1.根据模板生成word文件(word文件模板在resources里面)

    2.生成好的文件进行上传,上传分两步:具体实现看uploadMFile方法

    3.查询文件上传状态

    4.获取文件坐标

    5.创建签署流程,返回签署流程id
    
    6.最后返回页面签署路径url,返回给前端用于给用户访问签署的页面

3.后端代码实现:

  /**
     * 签署完成后回调通知
     * @return
     */
    @PostMapping("/returnNotify")
    public Map<String, String> returnNotify(@RequestBody ReturnNotifyDto returnNotifyDto){
        Map<String, String> result = baseAuthInfoService.returnNotify(returnNotifyDto);

        return result;
    }

    /**
     * 签署回调通知
     *
     * @param returnNotifyDto
     */
    @Override
    public Map<String, String> returnNotify(ReturnNotifyDto returnNotifyDto) {
        boolean update = false;
        String action = returnNotifyDto.getAction();
        if ("SIGN_MISSON_COMPLETE".equalsIgnoreCase(action)) {
            // 签署流程id
            String signFlowId = returnNotifyDto.getSignFlowId();
            // 签署描述
            // todo 省略代码,完整代码见课程资料中的完整代码
            // signResult 签署结果 2 - 签署完成,4 - 拒签
            // todo 省略代码,完整代码见课程资料中的完整代码
            // baseAuthInfoEntity.setAuthFlowId(signFlowId);
            baseAuthInfoEntity.setAuthStatus(signResult);
            baseAuthInfoEntity.setAuthExplain(resultDescription);
            LambdaUpdateWrapper<BaseAuthInfoEntity> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(BaseAuthInfoEntity::getAuthFlowId, signFlowId);
            update = this.update(baseAuthInfoEntity, updateWrapper);
        }

        Map<String, String> result = new HashMap<>();
        if (update) {
            result.put("code", "200");
            result.put("msg", "success");
        } else {
            result.put("code", "201");
            result.put("msg", "success");
        }
        return result;
    }
package io.renren.modules.zhaopin.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import io.renren.common.esign.EsignFileBean;
import io.renren.common.esign.EsignHttpHelper;
import io.renren.common.esign.EsignHttpResponse;
import io.renren.common.esign.enums.EsignHeaderConstant;
import io.renren.common.esign.enums.EsignRequestType;
import io.renren.common.esign.exception.EsignDemoException;
import io.renren.common.utils.*;
import io.renren.modules.zhaopin.dao.BaseAuthInfoDao;
import io.renren.modules.zhaopin.entity.BackgroundCheckOrdersEntity;
import io.renren.modules.zhaopin.entity.BaseAuthInfoEntity;
import io.renren.modules.zhaopin.service.BackgroundCheckOrdersService;
import io.renren.modules.zhaopin.service.BaseAuthInfoService;
import io.renren.modules.zhaopin.util.HTTPHelper;
import io.renren.modules.zhaopin.util.IdWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;

@Slf4j
@Service("baseAuthInfoService")
public class BaseAuthInfoServiceImpl extends ServiceImpl<BaseAuthInfoDao, BaseAuthInfoEntity> implements BaseAuthInfoService {

    @Value("${esign.host}")
    private String host;
    @Value("${esign.appId}")
    private String appId;
    @Value("${esign.appSecret}")
    private String appSecret;
    @Value("${esign.noticeUrl}")
    private String noticeUrl;
    @Autowired
    private BackgroundCheckOrdersService backgroundCheckOrdersService;

    @Autowired
    private BaseAuthInfoService baseAuthInfoService;
    @Autowired
    private IdWorker idWorker;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<BaseAuthInfoEntity> page = this.page(
                new Query<BaseAuthInfoEntity>().getPage(params),
                new QueryWrapper<BaseAuthInfoEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 生成word文件
     *
     * @param reportId 报告id
     */
    private void generateWordFile(String reportId) {
        LambdaQueryWrapper<BackgroundCheckOrdersEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BackgroundCheckOrdersEntity::getReportId, reportId);
        BackgroundCheckOrdersEntity checkOrders = backgroundCheckOrdersService.getOne(queryWrapper);
        try {
            // todo 省略代码,完整代码见课程资料中的完整代码
            System.out.println("生成文档路径:" + wordPath);
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 上传本地文件
     *
     * @return
     */
    private Map<String, Object> uploadMFile() {
        String contentType = "application/pdf";

        String postUrl = "/v3/files/file-upload-url";
        String postAllUrl = host + postUrl;
        try {
            File file = ResourceUtils.getFile("classpath:最终版授权声明.docx");
            String path = file.getPath();
            String contentMD5 = ESignUtils.getFileContentMD5(path);
            // 计算签名拼接的url
            // 构建请求Body体
            // todo 省略代码,完整代码见课程资料中的完整代码
            System.out.println("文件上传成功,状态码:" + code);

            return resultMap;
        } catch (Exception e) {
            e.printStackTrace();
            String msg = MessageFormat.format("请求签名鉴权方式调用接口出现异常: {0}", e.getMessage());
            System.out.println(msg);
            throw new RuntimeException(e);
        }
    }



    private Map<String, Object> getStringObjectMapPost(JSONObject reqBodyObj, String postUrl, String postAllUrl) throws Exception {
        // 请求Body体数据
        String reqBodyData = reqBodyObj.toString();
        // 对请求Body体内的数据计算ContentMD5
        String contentMD5 = ESignUtils.getBodyContentMD5(reqBodyData);
        System.out.println("请求body数据:" + reqBodyData);
        System.out.println("body的md5值:" + contentMD5);

        // todo 省略代码,完整代码见课程资料中的完整代码

        String result = HTTPHelper.sendPOST(postAllUrl, reqBodyData, header, "UTF-8");
        JSONObject resultObj = JSONObject.parseObject(result);
        // json转map
        Map<String, Object> resultMap = (Map<String, Object>) JSONObject.toJavaObject(resultObj, Map.class);
        System.out.println("请求返回信息: " + resultObj.toString());
        System.out.println("请求返回信息: " + resultMap.toString());
        return resultMap;
    }

    /**
     * 创建签署流程发送请求API
     *
     * @param reqBodyObj 请求体
     * @param postUrl    url
     * @param postAllUrl 全url
     * @return 返回数据
     * @throws Exception 异常处理
     */
    private Map<String, Object> getCreatePostSign(JSONObject reqBodyObj, String postUrl, String postAllUrl) throws Exception {
        // 请求Body体数据
        String reqBodyData = reqBodyObj.toString();
        // 对请求Body体内的数据计算ContentMD5
        String contentMD5 = ESignUtils.getBodyContentMD5(reqBodyData);
        System.out.println("请求body数据:" + reqBodyData);
        System.out.println("body的md5值:" + contentMD5);

        // 构建待签名字符串
        String method = "POST";
        String accept = "*/*";
        String contentType = "application/json;charset=UTF-8";
        String date = "";
        String headers = "";

        StringBuffer sb = new StringBuffer();
        sb.append(method).append("\n").append(accept).append("\n").append(contentMD5).append("\n")
                .append(contentType).append("\n").append(date).append("\n");
        if ("".equals(headers)) {
            sb.append(headers).append(postUrl);
        } else {
            sb.append(headers).append("\n").append(postUrl);
        }

        // todo 省略代码,完整代码见课程资料中的完整代码
        
        JSONObject resultObj = JSONObject.parseObject(result);
        // json转map
        Map<String, Object> resultMap = (Map<String, Object>) JSONObject.toJavaObject(resultObj, Map.class);
        int code = (int) resultMap.get("code");
        if (code == 0) {
            Map<String, Object> dataMap = (Map<String, Object>) resultMap.get("data");
            // 签署流程id
            String signFlowId = (String) dataMap.get("signFlowId");
            resultMap.put("signFlowId", signFlowId);
        }
        System.out.println("请求返回信息: " + resultObj.toString());
        return resultMap;
    }

    /**
     * 签署流程id 获取签署页面链接
     *
     * @param signFlowId 签署流程id
     */
    private String getSignUrl(String signFlowId) {
        String postAllUrl = host + "/v3/sign-flow/" + signFlowId + "/sign-url";
        String postUrl = "/v3/sign-flow/" + signFlowId + "/sign-url";

        // todo 省略代码,完整代码见课程资料中的完整代码
        return "";
    }

    /**
     * 查询文件上传状态
     *
     * @param fileId 报告id
     * @return
     */
    private Integer getFileStatus(String fileId) throws Exception {
        String getAllUrl = host + "/v3/files/" + fileId;
        String postUrl = "/v3/files/" + fileId;
        JSONObject reqBodyObj = new JSONObject();
        reqBodyObj.put("fileId", fileId);

        // GET请求时ContentMD5为""
        String contentMD5 = "";

        // 构建待签名字符串
        String method = "GET";
        String accept = "*/*";
        String contentType = "application/json; charset=UTF-8";
        String date = "";
        String headers = "";

        StringBuffer sb = new StringBuffer();
        sb.append(method).append("\n").append(accept).append("\n").append(contentMD5).append("\n")
                .append(contentType).append("\n").append(date).append("\n");
        if ("".equals(headers)) {
            sb.append(headers).append(postUrl);
        } else {
            sb.append(headers).append("\n").append(postUrl);
        }

        // todo 省略代码,完整代码见课程资料中的完整代码

        System.out.println(resultMap);
        return (Integer) resultMap.get("code");
    }

    /**
     * 获取文件坐标
     */
    private Map<String, Object> getPosition(String fileId) {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        String postUrl = "/v3/files/" + fileId + "/keyword-positions";
        String postAllUrl = host + postUrl;

        JSONObject reqBodyObj = new JSONObject();
        reqBodyObj.put("fileId", fileId);
        List<String> keywords = new ArrayList<>();
        keywords.add("本人签名");
        reqBodyObj.put("keywords", keywords);
        try {
            Map<String, Object> map = getStringObjectMapPost(reqBodyObj, postUrl, postAllUrl);
            System.out.println(map);
            Map<String, Object> dataMap = (Map<String, Object>) map.get("data");
            if (dataMap == null) {
                throw new RuntimeException("解析失败");
            }
            // todo 省略代码,完整代码见课程资料中的完整代码
            return position;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 签署
     *
     * @param reportId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R sign(String reportId) {
        try {
            String id = idWorker.nextId() + "";

            // todo 省略代码,完整代码见课程资料中的完整代码;

            System.out.println(resultMap);
            /*
              1. 查看所上传文件的当前状态(转换pdf/html文件状态)文件名称和下载链接。
              2. 当返回的文件状态status值为 2 或 5 时,此文件才可以被应用到签署流程中。
             */
            Integer fileStatus = getFileStatus(fileId);
            if (fileStatus == 0) {
                Map<String, Object> position = getPosition(fileId);
                BigDecimal positionY = (BigDecimal) position.get("positionY");
                BigDecimal positionX = (BigDecimal) position.get("positionX");
                Integer pageNums = (Integer) position.get("pageNums");
                Map<String, Object> sign = createSign(fileId, reportId, positionX, positionY, pageNums);
                String signUrl = (String) sign.get("signUrl");
                String signFlowId = (String) sign.get("signFlowId");
                // 保存到数据库 fileUploadId
                BaseAuthInfoEntity baseAuthInfoEntity = new BaseAuthInfoEntity();
                baseAuthInfoEntity.setId(id);
                baseAuthInfoEntity.setOrderId(reportId);
                baseAuthInfoEntity.setAuthFlowId(signFlowId);
                baseAuthInfoEntity.setAuthUrl(signUrl);
                baseAuthInfoEntity.setCreateTime(LocalDateTime.now());
                baseAuthInfoEntity.setUpdateTime(LocalDateTime.now());
                baseAuthInfoEntity.setFileId(fileId);
                baseAuthInfoEntity.setFileUploadUrl(fileUploadUrl);
                baseAuthInfoService.save(baseAuthInfoEntity);
                return R.ok(sign);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return R.ok("签署流程失败");
    }

    /**
     * 创建签署流程
     */
    public Map<String, Object> createSign(String fileId, String reportId, BigDecimal positionX, BigDecimal positionY, Integer pageNums) {
        LambdaQueryWrapper<BackgroundCheckOrdersEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BackgroundCheckOrdersEntity::getReportId, reportId);
        BackgroundCheckOrdersEntity checkOrders = backgroundCheckOrdersService.getOne(queryWrapper);

        Map<String, Object> paramsMap = new HashMap<>();

        Map<String, Object> docsMap = new HashMap<>();
        // 待签署文件ID
        docsMap.put("fileName", "最终版授权声明.docx");
        docsMap.put("fileId", fileId);

        Map<String, Object> signFlowConfigMap = new HashMap<>();

        // todo 省略代码,完整代码见课程资料中的完整代码

        System.out.println("reqBodyObj:" + reqBodyObj);
        System.out.println("paramsMap:" + paramsMap);
        try {
            Map<String, Object> map = getCreatePostSign(reqBodyObj, postUrl, postAllUrl);
            String signFlowId = (String) map.get("signFlowId");
            // 签署流程id 获取签署页面链接
            String signUrl = getSignUrl(signFlowId);
            map.put("signUrl", signUrl);
            map.put("signFlowId", signFlowId);
            return map;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

工具类代码比较多,就不全部写出来了

4.前端界面

点击去授权后就会调用E签宝,然后到签署流程页面,签署完成后回调通知接口

前端代码环境:Vue3

我封装了一些组件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值