submitForSigning()
@Override
@Transactional(rollbackFor = Exception.class)
public ApiResult<ContractSubmitResponse> submitForSigning(String contractId) {
log.info("开始提交合同签署, 合同ID: {}", contractId);
// 1. 获取当前用户ID
String currentUserId = securityService.getCurrentUserId();
// 2. 查询合同信息
Contract contract = contractMapper.selectById(contractId);
if (contract == null) {
return ApiResult.failed("合同不存在");
}
// 3. 检查合同状态
if (contract.getContractStatus() != ContractStatus.DRAFT) {
return ApiResult.failed("只有草稿状态的合同可以提交签署");
}
// 4. 检查用户是否有权限提交合同(是否属于甲方企业)
if (!enterpriseService.isUserBelongToEnterprise(currentUserId, contract.getPartyAId())) {
return ApiResult.forbidden("您没有权限提交该合同");
}
// 5. 更新合同状态为待签署
contract.setContractStatus(ContractStatus.PENDING_SIGNING);
contract.setUpdateUser(currentUserId);
contract.setUpdateTime(new Date());
contractMapper.updateStatus(contract);
// 6. 创建签署任务
ContractSignTask taskA = new ContractSignTask();
taskA.setTaskId(IdGenerator.generateId());
taskA.setContractId(contractId);
taskA.setEnterpriseId(contract.getPartyAId());
taskA.setEnterpriseName(contract.getPartyAName());
taskA.setSignStatus(SignStatus.PENDING);
taskA.setCreateTime(new Date());
contractSignTaskMapper.insert(taskA);
ContractSignTask taskB = new ContractSignTask();
taskB.setTaskId(IdGenerator.generateId());
taskB.setContractId(contractId);
taskB.setEnterpriseId(contract.getPartyBId());
taskB.setEnterpriseName(contract.getPartyBName());
taskB.setSignStatus(SignStatus.PENDING);
taskB.setCreateTime(new Date());
contractSignTaskMapper.insert(taskB);
// 7. 发送签署通知
notificationService.sendContractSigningNotification(contract.getPartyAId(), contract);
notificationService.sendContractSigningNotification(contract.getPartyBId(), contract);
// 8. 记录合同操作日志
contractLogService.logContractOperation(
contractId,
ContractOperationType.SUBMIT,
"提交合同签署",
currentUserId);
// 9. 构建返回结果
ContractSubmitResponse response = new ContractSubmitResponse();
response.setContractId(contractId);
response.setContractStatus(ContractStatus.PENDING_SIGNING);
response.setSubmitTime(contract.getUpdateTime());
response.setSigningUrl("/api/v1/contract/sign/" + contractId);
log.info("合同提交签署成功, 合同ID: {}", contractId);
return ApiResult.success("合同已提交签署", response);
}
处理逻辑:
signContract()
@Override
@Transactional(rollbackFor = Exception.class)
public ApiResult<ContractSignResponse> signContract(String contractId, ContractSignRequest request) {
log.info("开始签署合同, 合同ID: {}", contractId);
// 1. 获取当前用户ID
String currentUserId = securityService.getCurrentUserId();
// 2. 查询合同信息
Contract contract = contractMapper.selectById(contractId);
if (contract == null) {
return ApiResult.failed("合同不存在");
}
// 3. 检查合同状态
if (contract.getContractStatus() != ContractStatus.PENDING_SIGNING) {
return ApiResult.failed("当前合同状态不允许签署");
}
// 4. 获取用户所属企业
String userEnterpriseId = enterpriseService.getUserEnterpriseId(currentUserId);
if (userEnterpriseId == null) {
return ApiResult.failed("用户未关联企业,无法签署合同");
}
// 5. 检查用户企业是否为合同相关方
boolean isPartyA = userEnterpriseId.equals(contract.getPartyAId());
boolean isPartyB = userEnterpriseId.equals(contract.getPartyBId());
if (!isPartyA && !isPartyB) {
return ApiResult.forbidden("您的企业不是合同签署方,无权签署");
}
// 6. 查询签署任务
ContractSignTask signTask = contractSignTaskMapper.selectByContractAndEnterprise(
contractId, userEnterpriseId);
if (signTask == null) {
return ApiResult.failed("未找到对应的签署任务");
}
if (signTask.getSignStatus() == SignStatus.SIGNED) {
return ApiResult.failed("您已经签署过该合同");
}
// 7. 验证短信验证码
if (!verificationCodeService.verify(currentUserId, request.getVerificationCode())) {
return ApiResult.failed("验证码错误或已过期");
}
// 8. 创建数字签名
String signature = digitalSignatureService.createSignature(
contractId,
userEnterpriseId,
request.getSignerName(),
request.getSignerPosition());
// 9. 更新签署任务状态
signTask.setSignStatus(SignStatus.SIGNED);
signTask.setSignerName(request.getSignerName());
signTask.setSignerPosition(request.getSignerPosition());
signTask.setSignMethod(request.getSignMethod());
signTask.setSignature(signature);
signTask.setSignTime(new Date());
signTask.setUpdateTime(new Date());
contractSignTaskMapper.update(signTask);
// 10. 检查所有签署任务是否完成
boolean allSigned = contractSignTaskMapper.checkAllSigned(contractId);
// 11. 如果所有方都已签署,更新合同状态为已签署
if (allSigned) {
contract.setContractStatus(ContractStatus.SIGNED);
contract.setUpdateUser(currentUserId);
contract.setUpdateTime(new Date());
contractMapper.updateStatus(contract);
// 12. 生成最终签署版合同
String signedContractFileId = contractFileService.generateSignedContractFile(contract, contractSignTaskMapper.selectByContractId(contractId));
contractMapper.updateSignedContractFile(contractId, signedContractFileId);
// 13. 发送合同签署完成通知
notificationService.sendContractSignedNotification(contract.getPartyAId(), contract);
notificationService.sendContractSignedNotification(contract.getPartyBId(), contract);
}
// 14. 记录合同操作日志
contractLogService.logContractOperation(
contractId,
ContractOperationType.SIGN,
(isPartyA ? "甲方" : "乙方") + "签署合同",
currentUserId);
// 15. 构建返回结果
ContractSignResponse response = new ContractSignResponse();
response.setContractId(contractId);
response.setContractStatus(allSigned ? ContractStatus.SIGNED : ContractStatus.PENDING_SIGNING);
response.setSignTime(signTask.getSignTime());
if (allSigned) {
response.setSignedUrl("/api/v1/contract/download/signed/" + contractId);
}
log.info("合同签署成功, 合同ID: {}, 企业ID: {}", contractId, userEnterpriseId);
return ApiResult.success("合同签署成功", response);
}
处理逻辑:
主要实现类:DigitalSignatureServiceImpl
@Service
@RequiredArgsConstructor
public class DigitalSignatureServiceImpl implements DigitalSignatureService {
private final EnterpriseInfoMapper enterpriseInfoMapper;
@Override
public String createSignature(String contractId, String enterpriseId, String signerName, String signerPosition) {
try {
// 1. 获取企业信息
EnterpriseInfo enterpriseInfo = enterpriseInfoMapper.selectById(enterpriseId);
if (enterpriseInfo == null) {
throw new BusinessException("企业信息不存在");
}
// 2. 构建签名数据
Map<String, Object> signData = new HashMap<>();
signData.put("contractId", contractId);
signData.put("enterpriseId", enterpriseId);
signData.put("enterpriseName", enterpriseInfo.getEnterpriseName());
signData.put("signerName", signerName);
signData.put("signerPosition", signerPosition);
signData.put("signTime", System.currentTimeMillis());
// 3. 创建数字签名
String signDataStr = JsonUtil.toJson(signData);
String signature = SignatureUtil.sign(signDataStr);
return signature;
} catch (Exception e) {
throw new BusinessException("创建数字签名失败:" + e.getMessage());
}
}
@Override
public boolean verifySignature(String signature, String contractId, String enterpriseId) {
try {
// 1. 获取企业信息
EnterpriseInfo enterpriseInfo = enterpriseInfoMapper.selectById(enterpriseId);
if (enterpriseInfo == null) {
return false;
}
// 2. 获取签署记录
ContractSignTask signTask = contractSignTaskMapper.selectByContractAndEnterprise(contractId, enterpriseId);
if (signTask == null) {
return false;
}
// 3. 验证签名
return SignatureUtil.verify(signature, signTask);
} catch (Exception e) {
return false;
}
}
}
主要实现类:ContractFileServiceImpl
@Service
@RequiredArgsConstructor
public class ContractFileServiceImpl implements ContractFileService {
private final FileStorageService fileStorageService;
private final ContractTemplateService contractTemplateService;
@Override
public String generateContractFile(Contract contract) {
try {
// 1. 获取合同模板
String templateId = getTemplateIdByContractType(contract.getContractType());
byte[] templateContent = contractTemplateService.getTemplateContent(templateId);
// 2. 填充合同内容
Map<String, Object> templateData = buildTemplateData(contract);
byte[] contractContent = contractTemplateService.fillTemplate(templateContent, templateData);
// 3. 保存合同文件
String fileName = "合同_" + contract.getContractId() + ".pdf";
String fileId = fileStorageService.storeFile(contractContent, "application/pdf", fileName);
return fileId;
} catch (Exception e) {
throw new BusinessException("生成合同文件失败:" + e.getMessage());
}
}
@Override
public String generateSignedContractFile(Contract contract, List<ContractSignTask> signTasks) {
try {
// 1. 获取原合同文件
byte[] originalContract = fileStorageService.getFileContent(contract.getContractFileId());
// 2. 添加签名信息
byte[] signedContract = addSignaturesToContract(originalContract, signTasks);
// 3. 保存签署后的合同文件
String fileName = "已签署合同_" + contract.getContractId() + ".pdf";
String fileId = fileStorageService.storeFile(signedContract, "application/pdf", fileName);
return fileId;
} catch (Exception e) {
throw new BusinessException("生成已签署合同文件失败:" + e.getMessage());
}
}
@Override
public byte[] getContractFileContent(String fileId) {
return fileStorageService.getFileContent(fileId);
}
private String getTemplateIdByContractType(ContractType contractType) {
switch (contractType) {
case PURCHASE:
return "TEMPLATE_PURCHASE";
case SALES:
return "TEMPLATE_SALES";
case FINANCING:
return "TEMPLATE_FINANCING";
default:
return "TEMPLATE_GENERAL";
}
}
private Map<String, Object> buildTemplateData(Contract contract) {
Map<String, Object> data = new HashMap<>();
data.put("contractId", contract.getContractId());
data.put("contractName", contract.getContractName());
data.put("partyA", contract.getPartyAName());
data.put("partyB", contract.getPartyBName());
data.put("contractAmount", contract.getContractAmount());
data.put("startDate", DateUtil.formatDate(contract.getStartDate()));
data.put("endDate", DateUtil.formatDate(contract.getEndDate()));
data.put("contractPeriod", contract.getContractPeriod());
data.put("description", contract.getDescription());
data.put("createDate", DateUtil.formatDate(contract.getCreateTime()));
return data;
}
private byte[] addSignaturesToContract(byte[] originalContract, List<ContractSignTask> signTasks) {
// 使用PDF库添加数字签名、签章等信息
// 此处省略具体PDF处理实现...
return originalContract;
}
}
主要实现类:ContractMapper
@Mapper
public interface ContractMapper {
/**
* 插入合同
*/
int insert(Contract contract);
/**
* 根据ID查询
*/
Contract selectById(String contractId);
/**
* 更新合同状态
*/
int updateStatus(Contract contract);
/**
* 更新合同文件ID
*/
int updateContractFile(String contractId, String contractFileId);
/**
* 更新已签署合同文件ID
*/
int updateSignedContractFile(String contractId, String signedContractFileId);
/**
* 查询企业相关的合同列表
*/
List<Contract> selectByEnterpriseId(String enterpriseId, ContractQueryParam param);
/**
* 统计企业相关的合同数量
*/
int countByEnterpriseId(String enterpriseId, ContractQueryParam param);
/**
* 删除合同
*/
int deleteById(String contractId);
}
主要实现类:ContractSignTaskMapper
@Mapper
public interface ContractSignTaskMapper {
/**
* 插入签署任务
*/
int insert(ContractSignTask task);
/**
* 更新签署任务
*/
int update(ContractSignTask task);
/**
* 根据合同ID和企业ID查询签署任务
*/
ContractSignTask selectByContractAndEnterprise(String contractId, String enterpriseId);
/**
* 根据合同ID查询所有签署任务
*/
List<ContractSignTask> selectByContractId(String contractId);
/**
* 检查合同是否已全部签署
*/
boolean checkAllSigned(String contractId);
/**
* 删除签署任务
*/
int deleteByContractId(String contractId);
}
主要实现类:ContractLogMapper
@Mapper
public interface ContractLogMapper {
/**
* 插入合同操作日志
*/
int insert(ContractLog log);
/**
* 根据合同ID查询操作日志
*/
List<ContractLog> selectByContractId(String contractId);
}