本文目录导读:

我将为您提供一个完整的Java电子合同实现案例,包含合同创建、签名、验证等核心功能。
项目结构
electronic-contract/
├── pom.xml
├── src/main/java/com/example/contract/
│ ├── model/
│ │ ├── Contract.java
│ │ ├── ContractStatus.java
│ │ └── Signature.java
│ ├── service/
│ │ ├── ContractService.java
│ │ ├── SignatureService.java
│ │ └── VerificationService.java
│ ├── util/
│ │ ├── DigitalSignatureUtil.java
│ │ └── EncryptionUtil.java
│ ├── controller/
│ │ └── ContractController.java
│ └── ContractApplication.java
Maven配置 (pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
</parent>
<groupId>com.example</groupId>
<artifactId>electronic-contract</artifactId>
<version>1.0.0</version>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- H2 Database (示例用) -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Bouncy Castle 加密库 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.70</version>
</dependency>
<!-- PDF处理 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13.2</version>
</dependency>
</dependencies>
</project>
实体类
Contract.java
package com.example.contract.model;
import lombok.Data;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Data
@Entity
@Table(name = "contracts")
public class Contract {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String contractNo;
@Column(nullable = false)
private String title;
@Column(length = 2000)
private String content;
@Enumerated(EnumType.STRING)
private ContractStatus status;
@Column(name = "created_at")
private LocalDateTime createdAt;
@Column(name = "updated_at")
private LocalDateTime updatedAt;
@Column(name = "created_by")
private String createdBy;
@Column(name = "signature_path")
private String signaturePath;
@Column(name = "hash_value")
private String hashValue;
@OneToMany(mappedBy = "contract", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Signature> signatures = new ArrayList<>();
@PrePersist
protected void onCreate() {
createdAt = LocalDateTime.now();
updatedAt = LocalDateTime.now();
}
@PreUpdate
protected void onUpdate() {
updatedAt = LocalDateTime.now();
}
}
ContractStatus.java
package com.example.contract.model;
public enum ContractStatus {
DRAFT("草稿"),
PENDING_SIGNATURE("待签署"),
PARTIALLY_SIGNED("部分签署"),
COMPLETED("已完成"),
EXPIRED("已过期"),
CANCELLED("已取消");
private final String description;
ContractStatus(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
Signature.java
package com.example.contract.model;
import lombok.Data;
import javax.persistence.*;
import java.time.LocalDateTime;
@Data
@Entity
@Table(name = "signatures")
public class Signature {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "contract_id", nullable = false)
private Contract contract;
@Column(nullable = false)
private String signerName;
@Column(nullable = false)
private String signerId;
@Column(name = "signature_data", columnDefinition = "TEXT")
private String signatureData;
@Column(name = "certificate")
private String certificate;
@Column(name = "signed_at")
private LocalDateTime signedAt;
@Column(name = "ip_address")
private String ipAddress;
@Column(name = "hash_value")
private String hashValue;
@PrePersist
protected void onCreate() {
signedAt = LocalDateTime.now();
}
}
工具类
DigitalSignatureUtil.java
package com.example.contract.util;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
@Component
public class DigitalSignatureUtil {
private static final String ALGORITHM = "RSA";
private static final String SIGNATURE_ALGORITHM = "SHA256withRSA";
private static final int KEY_SIZE = 2048;
@PostConstruct
public void init() {
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.addProvider(new BouncyCastleProvider());
}
}
/**
* 生成密钥对
*/
public KeyPair generateKeyPair() throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM);
keyGen.initialize(KEY_SIZE);
return keyGen.generateKeyPair();
}
/**
* 签名
*/
public String sign(String data, PrivateKey privateKey) throws Exception {
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initSign(privateKey);
signature.update(data.getBytes());
byte[] signatureBytes = signature.sign();
return Base64.getEncoder().encodeToString(signatureBytes);
}
/**
* 验证签名
*/
public boolean verify(String data, String signatureStr, PublicKey publicKey) throws Exception {
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initVerify(publicKey);
signature.update(data.getBytes());
byte[] signatureBytes = Base64.getDecoder().decode(signatureStr);
return signature.verify(signatureBytes);
}
/**
* 私钥转字符串
*/
public String privateKeyToString(PrivateKey privateKey) {
return Base64.getEncoder().encodeToString(privateKey.getEncoded());
}
/**
* 公钥转字符串
*/
public String publicKeyToString(PublicKey publicKey) {
return Base64.getEncoder().encodeToString(publicKey.getEncoded());
}
/**
* 字符串转私钥
*/
public PrivateKey stringToPrivateKey(String keyStr) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(keyStr);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
return keyFactory.generatePrivate(spec);
}
/**
* 字符串转公钥
*/
public PublicKey stringToPublicKey(String keyStr) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(keyStr);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
return keyFactory.generatePublic(spec);
}
}
EncryptionUtil.java
package com.example.contract.util;
import org.springframework.stereotype.Component;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.util.Base64;
@Component
public class EncryptionUtil {
private static final String AES_ALGORITHM = "AES";
private static final String HASH_ALGORITHM = "SHA-256";
/**
* 生成AES密钥
*/
public SecretKey generateAESKey() throws Exception {
KeyGenerator keyGen = KeyGenerator.getInstance(AES_ALGORITHM);
keyGen.init(256);
return keyGen.generateKey();
}
/**
* AES加密
*/
public String aesEncrypt(String data, SecretKey key) throws Exception {
Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encrypted = cipher.doFinal(data.getBytes());
return Base64.getEncoder().encodeToString(encrypted);
}
/**
* AES解密
*/
public String aesDecrypt(String encryptedData, SecretKey key) throws Exception {
Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
return new String(decrypted);
}
/**
* 计算哈希值
*/
public String calculateHash(String data) throws Exception {
MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM);
byte[] hash = digest.digest(data.getBytes());
return Base64.getEncoder().encodeToString(hash);
}
}
服务类
ContractService.java
package com.example.contract.service;
import com.example.contract.model.Contract;
import com.example.contract.model.ContractStatus;
import com.example.contract.model.Signature;
import com.example.contract.repository.ContractRepository;
import com.example.contract.util.DigitalSignatureUtil;
import com.example.contract.util.EncryptionUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.UUID;
@Service
@RequiredArgsConstructor
public class ContractService {
private final ContractRepository contractRepository;
private final DigitalSignatureUtil digitalSignatureUtil;
private final EncryptionUtil encryptionUtil;
/**
* 创建合同
*/
@Transactional
public Contract createContract(String title, String content, String userId) {
Contract contract = new Contract();
contract.setContractNo(generateContractNo());
contract.setTitle(title);
contract.setContent(content);
contract.setStatus(ContractStatus.DRAFT);
contract.setCreatedBy(userId);
// 计算合同内容哈希值
try {
String hash = encryptionUtil.calculateHash(content);
contract.setHashValue(hash);
} catch (Exception e) {
throw new RuntimeException("计算合同哈希值失败", e);
}
return contractRepository.save(contract);
}
/**
* 获取合同
*/
public Contract getContract(Long id) {
return contractRepository.findById(id)
.orElseThrow(() -> new RuntimeException("合同不存在: " + id));
}
/**
* 获取合同列表
*/
public List<Contract> getAllContracts() {
return contractRepository.findAll();
}
/**
* 更新合同内容
*/
@Transactional
public Contract updateContract(Long id, String title, String content) {
Contract contract = getContract(id);
// 检查合同状态
if (contract.getStatus() != ContractStatus.DRAFT) {
throw new RuntimeException("只有草稿状态的合同才能修改");
}
contract.setTitle(title);
contract.setContent(content);
// 更新哈希值
try {
contract.setHashValue(encryptionUtil.calculateHash(content));
} catch (Exception e) {
throw new RuntimeException("更新哈希值失败", e);
}
return contractRepository.save(contract);
}
/**
* 发布合同(进入待签署状态)
*/
@Transactional
public Contract publishContract(Long id) {
Contract contract = getContract(id);
if (contract.getStatus() != ContractStatus.DRAFT) {
throw new RuntimeException("只有草稿状态的合同才能发布");
}
contract.setStatus(ContractStatus.PENDING_SIGNATURE);
return contractRepository.save(contract);
}
/**
* 签署合同
*/
@Transactional
public Contract signContract(Long contractId, String signerName,
String signerId, String signatureData) {
Contract contract = getContract(contractId);
// 验证合同状态
if (contract.getStatus() != ContractStatus.PENDING_SIGNATURE &&
contract.getStatus() != ContractStatus.PARTIALLY_SIGNED) {
throw new RuntimeException("合同当前状态不允许签署");
}
// 创建签名记录
Signature signature = new Signature();
signature.setContract(contract);
signature.setSignerName(signerName);
signature.setSignerId(signerId);
signature.setSignatureData(signatureData);
signature.setSignedAt(LocalDateTime.now());
// 验证签名数据(示例中简单验证非空)
if (signatureData == null || signatureData.trim().isEmpty()) {
throw new RuntimeException("签名数据无效");
}
contract.getSignatures().add(signature);
// 更新合同状态
// 这里假设需要双方签署完成,实际业务根据需求调整
if (contract.getSignatures().size() >= 2) {
contract.setStatus(ContractStatus.COMPLETED);
} else {
contract.setStatus(ContractStatus.PARTIALLY_SIGNED);
}
return contractRepository.save(contract);
}
/**
* 取消合同
*/
@Transactional
public Contract cancelContract(Long id) {
Contract contract = getContract(id);
if (contract.getStatus() == ContractStatus.COMPLETED) {
throw new RuntimeException("已完成的合同不能取消");
}
contract.setStatus(ContractStatus.CANCELLED);
return contractRepository.save(contract);
}
/**
* 生成合同编号
*/
private String generateContractNo() {
String dateStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
String uuid = UUID.randomUUID().toString().substring(0, 8).toUpperCase();
return "CT-" + dateStr + "-" + uuid;
}
}
SignatureService.java
package com.example.contract.service;
import com.example.contract.model.Contract;
import com.example.contract.model.Signature;
import com.example.contract.repository.SignatureRepository;
import com.example.contract.util.DigitalSignatureUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.security.KeyPair;
import java.util.List;
@Service
@RequiredArgsConstructor
public class SignatureService {
private final SignatureRepository signatureRepository;
private final DigitalSignatureUtil digitalSignatureUtil;
/**
* 获取合同的所有签名
*/
public List<Signature> getSignaturesByContract(Long contractId) {
return signatureRepository.findByContractIdOrderBySignedAtAsc(contractId);
}
/**
* 创建数字签名
*/
@Transactional
public Signature createDigitalSignature(Contract contract, String signerName,
String signerId, String ipAddress) {
try {
// 生成密钥对
KeyPair keyPair = digitalSignatureUtil.generateKeyPair();
// 创建签名记录
Signature signature = new Signature();
signature.setContract(contract);
signature.setSignerName(signerName);
signature.setSignerId(signerId);
signature.setIpAddress(ipAddress);
// 计算合同数据的签名
String dataToSign = buildDataToSign(contract, signerName, signerId);
String signatureStr = digitalSignatureUtil.sign(
dataToSign, keyPair.getPrivate());
signature.setSignatureData(signatureStr);
signature.setCertificate(digitalSignatureUtil.publicKeyToString(keyPair.getPublic()));
// 计算签名哈希
signature.setHashValue(calculateSignatureHash(signatureStr));
return signatureRepository.save(signature);
} catch (Exception e) {
throw new RuntimeException("创建数字签名失败", e);
}
}
/**
* 验证签名
*/
public boolean verifySignature(Signature signature, Contract contract) {
try {
// 重建签名的数据
String dataToVerify = buildDataToSign(contract,
signature.getSignerName(), signature.getSignerId());
// 获取公钥
var publicKey = digitalSignatureUtil.stringToPublicKey(
signature.getCertificate());
// 验证签名
return digitalSignatureUtil.verify(
dataToVerify, signature.getSignatureData(), publicKey);
} catch (Exception e) {
throw new RuntimeException("验证签名失败", e);
}
}
/**
* 构建待签名数据
*/
private String buildDataToSign(Contract contract, String signerName, String signerId) {
return String.format("%s|%s|%s|%s",
contract.getContractNo(),
contract.getContent(),
signerName,
signerId);
}
/**
* 计算签名哈希值
*/
private String calculateSignatureHash(String signature) {
try {
java.security.MessageDigest digest =
java.security.MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(signature.getBytes());
return java.util.Base64.getEncoder().encodeToString(hash);
} catch (Exception e) {
throw new RuntimeException("计算哈希失败", e);
}
}
}
VerificationService.java
package com.example.contract.service;
import com.example.contract.model.Contract;
import com.example.contract.model.Signature;
import com.example.contract.util.EncryptionUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@RequiredArgsConstructor
public class VerificationService {
private final ContractService contractService;
private final SignatureService signatureService;
private final EncryptionUtil encryptionUtil;
/**
* 验证合同完整性
*/
public boolean verifyContractIntegrity(Long contractId) {
Contract contract = contractService.getContract(contractId);
try {
// 重新计算合同内容哈希值
String currentHash = encryptionUtil.calculateHash(contract.getContent());
// 与保存的哈希值比较
return currentHash.equals(contract.getHashValue());
} catch (Exception e) {
throw new RuntimeException("验证合同完整性失败", e);
}
}
/**
* 验证所有签名
*/
public boolean verifyAllSignatures(Long contractId) {
Contract contract = contractService.getContract(contractId);
List<Signature> signatures = signatureService.getSignaturesByContract(contractId);
if (signatures.isEmpty()) {
return false;
}
// 验证所有签名
for (Signature signature : signatures) {
boolean isValid = signatureService.verifySignature(signature, contract);
if (!isValid) {
return false;
}
}
return true;
}
/**
* 获取合同验证报告
*/
public String generateVerificationReport(Long contractId) {
Contract contract = contractService.getContract(contractId);
StringBuilder report = new StringBuilder();
report.append("===== 合同验证报告 =====\n");
report.append("合同编号: ").append(contract.getContractNo()).append("\n");
report.append("合同标题: ").append(contract.getTitle()).append("\n");
report.append("合同状态: ").append(contract.getStatus().getDescription()).append("\n");
report.append("创建时间: ").append(contract.getCreatedAt()).append("\n\n");
// 验证完整性
boolean integrity = verifyContractIntegrity(contractId);
report.append("内容完整性: ").append(integrity ? "通过" : "未通过").append("\n");
// 验证签名
List<Signature> signatures = signatureService.getSignaturesByContract(contractId);
report.append("签名数量: ").append(signatures.size()).append("\n");
for (int i = 0; i < signatures.size(); i++) {
Signature sig = signatures.get(i);
boolean valid = signatureService.verifySignature(sig, contract);
report.append("签名").append(i + 1).append(": ")
.append(sig.getSignerName())
.append(" - ").append(valid ? "有效" : "无效")
.append("\n");
}
return report.toString();
}
}
控制器
ContractController.java
package com.example.contract.controller;
import com.example.contract.model.Contract;
import com.example.contract.model.Signature;
import com.example.contract.service.ContractService;
import com.example.contract.service.SignatureService;
import com.example.contract.service.VerificationService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/contracts")
@RequiredArgsConstructor
public class ContractController {
private final ContractService contractService;
private final SignatureService signatureService;
private final VerificationService verificationService;
/**
* 创建合同
*/
@PostMapping
public ResponseEntity<?> createContract(@RequestBody Map<String, String> request) {
try {
Contract contract = contractService.createContract(
request.get("title"),
request.get("content"),
request.get("userId")
);
return ResponseEntity.ok(contract);
} catch (Exception e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
/**
* 获取所有合同
*/
@GetMapping
public ResponseEntity<List<Contract>> getAllContracts() {
return ResponseEntity.ok(contractService.getAllContracts());
}
/**
* 获取合同详情
*/
@GetMapping("/{id}")
public ResponseEntity<?> getContract(@PathVariable Long id) {
try {
Contract contract = contractService.getContract(id);
return ResponseEntity.ok(contract);
} catch (Exception e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
/**
* 更新合同
*/
@PutMapping("/{id}")
public ResponseEntity<?> updateContract(@PathVariable Long id,
@RequestBody Map<String, String> request) {
try {
Contract contract = contractService.updateContract(
id, request.get("title"), request.get("content"));
return ResponseEntity.ok(contract);
} catch (Exception e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
/**
* 发布合同
*/
@PostMapping("/{id}/publish")
public ResponseEntity<?> publishContract(@PathVariable Long id) {
try {
Contract contract = contractService.publishContract(id);
return ResponseEntity.ok(contract);
} catch (Exception e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
/**
* 签署合同
*/
@PostMapping("/{id}/sign")
public ResponseEntity<?> signContract(@PathVariable Long id,
@RequestBody Map<String, String> request) {
try {
Contract contract = contractService.signContract(
id,
request.get("signerName"),
request.get("signerId"),
request.get("signatureData")
);
return ResponseEntity.ok(contract);
} catch (Exception e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
/**
* 创建数字签名(带证书)
*/
@PostMapping("/{id}/digital-signature")
public ResponseEntity<?> createDigitalSignature(@PathVariable Long id,
@RequestBody Map<String, String> request) {
try {
Contract contract = contractService.getContract(id);
Signature signature = signatureService.createDigitalSignature(
contract,
request.get("signerName"),
request.get("signerId"),
request.get("ipAddress")
);
return ResponseEntity.ok(signature);
} catch (Exception e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
/**
* 获取合同签名列表
*/
@GetMapping("/{id}/signatures")
public ResponseEntity<List<Signature>> getSignatures(@PathVariable Long id) {
return ResponseEntity.ok(signatureService.getSignaturesByContract(id));
}
/**
* 验证合同
*/
@GetMapping("/{id}/verify")
public ResponseEntity<?> verifyContract(@PathVariable Long id) {
try {
Map<String, Object> result = new HashMap<>();
result.put("integrity", verificationService.verifyContractIntegrity(id));
result.put("signaturesValid", verificationService.verifyAllSignatures(id));
result.put("report", verificationService.generateVerificationReport(id));
return ResponseEntity.ok(result);
} catch (Exception e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
/**
* 取消合同
*/
@PostMapping("/{id}/cancel")
public ResponseEntity<?> cancelContract(@PathVariable Long id) {
try {
Contract contract = contractService.cancelContract(id);
return ResponseEntity.ok(contract);
} catch (Exception e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
}
主应用程序
ContractApplication.java
package com.example.contract;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ContractApplication {
public static void main(String[] args) {
SpringApplication.run(ContractApplication.class, args);
}
}
测试用例
ContractServiceTest.java
package com.example.contract;
import com.example.contract.model.Contract;
import com.example.contract.model.ContractStatus;
import com.example.contract.service.ContractService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
public class ContractServiceTest {
@Autowired
private ContractService contractService;
@Test
public void testCreateAndPublishContract() {
// 创建合同
Contract contract = contractService.createContract(
"测试合同", "合同内容", "user001");
assertNotNull(contract);
assertNotNull(contract.getContractNo());
assertEquals(ContractStatus.DRAFT, contract.getStatus());
// 发布合同
Contract published = contractService.publishContract(contract.getId());
assertEquals(ContractStatus.PENDING_SIGNATURE, published.getStatus());
}
@Test
public void testSignContract() {
// 创建并发布合同
Contract contract = contractService.createContract(
"签署测试", "合同内容", "user001");
contractService.publishContract(contract.getId());
// 第一次签署
Contract signed1 = contractService.signContract(
contract.getId(), "张三", "1001", "signature1");
assertEquals(ContractStatus.PARTIALLY_SIGNED, signed1.getStatus());
// 第二次签署
Contract signed2 = contractService.signContract(
contract.getId(), "李四", "1002", "signature2");
assertEquals(ContractStatus.COMPLETED, signed2.getStatus());
}
@Test
public void testVerifyContractIntegrity() {
// 创建合同
Contract contract = contractService.createContract(
"完整性测试", "原始内容", "user001");
// 这里假设有注入VerificationService
// 验证通过
assertNotNull(contract);
}
}
配置文��
application.yml
server:
port: 8080
spring:
application:
name: electronic-contract
datasource:
url: jdbc:h2:mem:contractdb
driver-class-name: org.h2.Driver
username: sa
password:
jpa:
database-platform: org.hibernate.dialect.H2Dialect
hibernate:
ddl-auto: create-drop
show-sql: true
h2:
console:
enabled: true
path: /h2-console
management:
endpoints:
web:
exposure:
include: health,info
API示例
# 创建合同
curl -X POST http://localhost:8080/api/contracts \
-H "Content-Type: application/json" \
-d '{: "劳动合同",
"content": "甲乙双方经平等协商,达成如下协议...",
"userId": "user001"
}'
# 发布合同
curl -X POST http://localhost:8080/api/contracts/1/publish
# 签署合同
curl -X POST http://localhost:8080/api/contracts/1/sign \
-H "Content-Type: application/json" \
-d '{
"signerName": "张三",
"signerId": "1001",
"signatureData": "base64签名数据"
}'
# 验证合同
curl http://localhost:8080/api/contracts/1/verify
这个电子合同系统实现了:
- 合同管理:创建、修改、发布、取消
- 数字签名:支持电子签名和数字证书签名
- 完整性验证:通过哈希值验证合同内容未被篡改
- 签名验证:验证签名的真实性和有效性
- 状态管理:合同全生命周期状态追踪
可以根据实际业务需求扩展更多功能,如PDF生成、合同模板、审批流程等。