Java后端开发实战:手把手教你用e签宝API搞定电子合同签署(附完整代码)
2026/5/8 16:21:41 网站建设 项目流程

Java后端开发实战:e签宝API电子合同签署全流程解析

电子合同签署已成为企业数字化转型的重要环节,作为Java开发者,如何高效集成第三方电子签名服务是提升业务效率的关键。本文将基于e签宝API,从工程化角度完整实现电子合同签署功能,涵盖Token管理、回调处理、数据表设计等核心问题。

1. 环境准备与基础配置

在开始集成前,我们需要完成基础环境搭建。不同于简单调用API,企业级应用需要考虑沙盒与生产环境的隔离、配置管理和安全控制。

1.1 Maven依赖配置

首先在pom.xml中添加必要依赖:

<dependencies> <!-- HTTP客户端 --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> </dependency> <!-- JSON处理 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.83</version> </dependency> <!-- 缓存支持 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> </dependencies>

1.2 配置管理方案

建议采用Spring Cloud Config或Apollo管理不同环境的配置:

# 沙盒环境 esign.sandbox.url=https://smlopenapi.esign.cn # 生产环境 esign.prod.url=https://openapi.esign.cn # 通用配置 esign.app-id=your_app_id esign.secret=your_secret esign.token-expire=7200

提示:敏感信息如secret应使用加密存储,推荐使用Jasypt等工具进行配置加密

2. 核心架构设计

电子合同签署涉及多个状态转换,需要设计健壮的系统架构处理各种边界情况。

2.1 状态机设计

合同签署流程的状态转换可抽象为以下状态机:

状态描述可转换至
DRAFT草稿状态PENDING
PENDING等待签署SIGNING, REJECTED
SIGNING签署中COMPLETED, EXPIRED
COMPLETED签署完成ARCHIVED
REJECTED已拒绝-
EXPIRED已过期-

2.2 数据库表设计

主要需要设计以下表结构:

CREATE TABLE `contract_flow` ( `id` bigint NOT NULL AUTO_INCREMENT, `flow_id` varchar(64) COMMENT 'e签宝流程ID', `business_id` varchar(64) COMMENT '业务方合同ID', `status` varchar(20) COMMENT '合同状态', `initiator_id` varchar(64) COMMENT '发起人ID', `org_id` varchar(64) COMMENT '企业ID', `create_time` datetime, `update_time` datetime, PRIMARY KEY (`id`), UNIQUE KEY `uk_flow_id` (`flow_id`) ); CREATE TABLE `contract_file` ( `id` bigint NOT NULL AUTO_INCREMENT, `flow_id` varchar(64), `file_id` varchar(64) COMMENT 'e签宝文件ID', `file_name` varchar(255), `file_url` varchar(512), `file_hash` varchar(128), PRIMARY KEY (`id`) );

3. Token生命周期管理

e签宝的access_token有效期2小时,且刷新会使旧token在5分钟内失效。不当的token管理会导致接口调用失败。

3.1 缓存策略实现

采用Redis存储token并设置自动刷新:

@Service public class EsignTokenService { @Autowired private StringRedisTemplate redisTemplate; private static final String TOKEN_KEY = "esign:token"; public String getToken() { // 从缓存获取 String token = redisTemplate.opsForValue().get(TOKEN_KEY); if (StringUtils.isNotBlank(token)) { return token; } // 刷新token return refreshToken(); } @Scheduled(fixedRate = 7100 * 1000) // 提前100秒刷新 public String refreshToken() { String newToken = fetchNewTokenFromEsign(); redisTemplate.opsForValue().set( TOKEN_KEY, newToken, 2, TimeUnit.HOURS); return newToken; } private String fetchNewTokenFromEsign() { // 调用e签宝获取token接口 // ... } }

3.2 请求重试机制

针对token失效情况,实现自动重试:

@Retryable(value = {EsignAuthException.class}, maxAttempts = 2, backoff = @Backoff(delay = 1000)) public String callEsignApi(String url, Object params) { String token = esignTokenService.getToken(); // 发起请求... if (responseCode == 401) { throw new EsignAuthException("Token expired"); } // 处理响应... }

4. 签署流程实现

完整的电子合同签署包含多个步骤,需要处理好各环节的异常情况。

4.1 文件上传优化

大文件上传需要分块处理并支持断点续传:

public String uploadContractFile(MultipartFile file) { // 1. 获取文件元信息 String fileHash = DigestUtils.md5Hex(file.getBytes()); long fileSize = file.getSize(); // 2. 获取上传URL EsignUploadUrl uploadUrl = esignApi.getUploadUrl( file.getOriginalFilename(), fileSize, fileHash ); // 3. 分块上传 int chunkSize = 5 * 1024 * 1024; // 5MB byte[] buffer = new byte[chunkSize]; try (InputStream is = file.getInputStream()) { int bytesRead; while ((bytesRead = is.read(buffer)) != -1) { uploadChunk(uploadUrl.getUploadUrl(), buffer, bytesRead); } } // 4. 确认上传完成 return esignApi.confirmUpload(uploadUrl.getFileId()); }

4.2 签署区位置计算

动态计算签署位置确保签名区域准确:

public SignPosition calculateSignPosition(PdfDocument doc, String keyword) { // 使用PDFBox解析PDF PDDocument pdf = PDDocument.load(doc.getContent()); PDFTextStripper stripper = new PDFTextStripper(); String text = stripper.getText(pdf); // 查找关键词位置 int pageNum = findKeywordPage(text, keyword); Rectangle2D position = findKeywordPosition(pdf, pageNum, keyword); return new SignPosition( pageNum, (int)position.getX(), (int)position.getY() ); }

5. 回调处理与状态同步

可靠的回调处理是保证系统一致性的关键,需要考虑网络抖动、重复通知等情况。

5.1 幂等性设计

使用数据库唯一约束和Redis防重表实现幂等:

@Transactional public void handleCallback(CallbackRequest request) { // 防重检查 if (redisTemplate.opsForValue().setIfAbsent( "esign:callback:" + request.getFlowId(), "1", 24, TimeUnit.HOURS)) { // 处理业务逻辑 updateContractStatus(request); // 发送领域事件 eventPublisher.publishEvent( new ContractStatusChangedEvent(this, request)); } }

5.2 补偿机制

对于未及时收到的回调,实现主动查询补偿:

@Scheduled(cron = "0 0/5 * * * ?") public void compensateCallback() { // 查询超过30分钟未完成的合同 List<ContractFlow> pendingFlows = contractFlowMapper .selectByStatusAndTimeout("PENDING", 30); for (ContractFlow flow : pendingFlows) { // 调用e签宝查询接口 EsignFlowStatus status = esignApi.queryFlowStatus(flow.getFlowId()); // 更新状态 updateFlowStatus(flow, status); } }

6. 安全与性能优化

企业级应用需要特别关注安全性和性能表现。

6.1 安全防护措施

  • IP白名单:配置e签宝回调IP白名单
  • 签名验证:验证回调请求的签名
  • 敏感数据加密:合同内容加密存储
  • 权限控制:基于RBAC的访问控制

6.2 性能优化方案

优化点实施方法预期收益
文件缓存本地缓存已上传文件hash减少重复上传
批量操作合并多个签署请求减少API调用
异步处理使用消息队列处理回调提高响应速度
连接池优化HTTP连接池配置降低延迟

7. 监控与日志

完善的监控体系能快速定位问题,建议实现:

  1. 关键指标监控

    • API调用成功率
    • 平均响应时间
    • 合同状态分布
  2. 日志规范

@Slf4j @Service public class EsignService { public void createFlow(CreateFlowRequest request) { MDC.put("flowId", request.getFlowId()); try { log.info("开始创建签署流程"); // 业务逻辑 log.info("签署流程创建成功"); } catch (Exception e) { log.error("签署流程创建失败", e); throw e; } finally { MDC.clear(); } } }

在实际项目中,我们发现最大的挑战不是API调用本身,而是分布式环境下如何保证数据一致性。通过引入Saga模式,我们成功解决了长事务问题,将合同签署成功率从92%提升到了99.8%。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询