# 签约功能生命周期详解 - 第二部分 ## 2. 服务层 (Service) (续) #### 2.1.2 提交合同签署流程 `submitForSigning()` ```java @Override @Transactional(rollbackFor = Exception.class) public ApiResult 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); } ``` **处理逻辑**: 1. 获取当前用户ID 2. 查询合同信息 3. 检查合同状态 4. 检查用户权限 5. 更新合同状态为待签署 6. 创建签署任务(甲方和乙方) 7. 发送签署通知 8. 记录合同操作日志 9. 构建返回结果 #### 2.1.3 合同签署流程 `signContract()` ```java @Override @Transactional(rollbackFor = Exception.class) public ApiResult 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); } ``` **处理逻辑**: 1. 获取当前用户ID 2. 查询合同信息 3. 检查合同状态 4. 获取用户所属企业 5. 检查用户企业是否为合同相关方 6. 查询签署任务 7. 验证短信验证码 8. 创建数字签名 9. 更新签署任务状态 10. 检查所有签署任务是否完成 11. 如果所有方都已签署,更新合同状态为已签署 12. 生成最终签署版合同 13. 发送合同签署完成通知 14. 记录合同操作日志 15. 构建返回结果 ### 2.2 数字签名服务 **主要实现类**:`DigitalSignatureServiceImpl` ```java @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 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; } } } ``` ### 2.3 合同文件服务 **主要实现类**:`ContractFileServiceImpl` ```java @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 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 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 buildTemplateData(Contract contract) { Map 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 signTasks) { // 使用PDF库添加数字签名、签章等信息 // 此处省略具体PDF处理实现... return originalContract; } } ``` ## 3. 数据访问层 (Mapper/Repository) ### 3.1 合同数据访问 **主要实现类**:`ContractMapper` ```java @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 selectByEnterpriseId(String enterpriseId, ContractQueryParam param); /** * 统计企业相关的合同数量 */ int countByEnterpriseId(String enterpriseId, ContractQueryParam param); /** * 删除合同 */ int deleteById(String contractId); } ``` ### 3.2 合同签署任务数据访问 **主要实现类**:`ContractSignTaskMapper` ```java @Mapper public interface ContractSignTaskMapper { /** * 插入签署任务 */ int insert(ContractSignTask task); /** * 更新签署任务 */ int update(ContractSignTask task); /** * 根据合同ID和企业ID查询签署任务 */ ContractSignTask selectByContractAndEnterprise(String contractId, String enterpriseId); /** * 根据合同ID查询所有签署任务 */ List selectByContractId(String contractId); /** * 检查合同是否已全部签署 */ boolean checkAllSigned(String contractId); /** * 删除签署任务 */ int deleteByContractId(String contractId); } ``` ### 3.3 合同操作日志数据访问 **主要实现类**:`ContractLogMapper` ```java @Mapper public interface ContractLogMapper { /** * 插入合同操作日志 */ int insert(ContractLog log); /** * 根据合同ID查询操作日志 */ List selectByContractId(String contractId); } ```