本文目录导读:

我将为您提供一个完整的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生成、合同模板、审批流程等。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播