package com.ymwk.icvp.signature.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ymwk.icvp.component.PageResult; import com.ymwk.icvp.component.ResponseDTO; import com.ymwk.icvp.pki.entity.CertificateEntity; import com.ymwk.icvp.pki.mapper.CertificateMapper; import com.ymwk.icvp.signature.entity.Picture; import com.ymwk.icvp.signature.entity.Seal; import com.ymwk.icvp.signature.mapper.PictureMapper; import com.ymwk.icvp.signature.mapper.SealMapper; import com.ymwk.icvp.signature.model.*; import com.ymwk.icvp.signature.service.SealBuilder; import com.ymwk.icvp.signature.service.SealService; import com.ymwk.icvp.signature.service.SealVerifyService; import com.ymwk.icvp.utils.MinioClientService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.time.LocalDateTime; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @Service @RequiredArgsConstructor public class SealServiceImpl implements SealService { private final SealMapper sealMapper; private final SealBuilder sealBuilder; private final SealVerifyService sealVerifyService; private final PictureMapper pictureMapper; private final CertificateMapper certificateMapper; private final MinioClientService minioClientService; @Override public ResponseDTO> pageSeal(SealQueryDTO queryDTO) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.like(StringUtils.hasText(queryDTO.getSealAlias()), Seal::getSealAlias, queryDTO.getSealAlias()) .eq(queryDTO.getSealStatus() != null, Seal::getSealStatus, queryDTO.getSealStatus()) .eq(StringUtils.hasText(queryDTO.getAlgorithm()), Seal::getAlgorithm, queryDTO.getAlgorithm()) .ge(queryDTO.getCreateTimeStart() != null, Seal::getCreateTime, queryDTO.getCreateTimeStart()) .le(queryDTO.getCreateTimeEnd() != null, Seal::getCreateTime, queryDTO.getCreateTimeEnd()) .orderByDesc(Seal::getCreateTime); Page page = sealMapper.selectPage( new Page<>(queryDTO.getPageNum(), queryDTO.getPageSize()), wrapper); PageResult result = new PageResult<>(); result.setTotal(page.getTotal()); result.setList(page.getRecords()); return ResponseDTO.ok(result); } @Override public ResponseDTO getSealDetail(Long id) { Seal seal = sealMapper.selectById(id); /* if (seal == null) { return ResponseDTO.error(100001,"印章不存在"); }*/ return ResponseDTO.ok(seal); } @Override @Transactional(rollbackFor = Exception.class) public ResponseDTO createSeal(SealCreateDTO createDTO) { // 1. 校验印章别名唯一 Long count = sealMapper.selectCount(new LambdaQueryWrapper() .eq(Seal::getSealAlias, createDTO.getSealAlias())); if (count > 0) { return ResponseDTO.error(ResponseDTO.ERROR_CODE, "印章别名已存在:" + createDTO.getSealAlias()); } // 2. 校验算法 SealAlgorithmEnum algorithm = SealAlgorithmEnum.of(createDTO.getAlgorithm()); if (algorithm == null) { return ResponseDTO.error(ResponseDTO.ERROR_CODE, "不支持的印章算法:" + createDTO.getAlgorithm()); } // 3. 查询印模和证书 Picture picture = pictureMapper.selectById(createDTO.getImpressionId()); if (picture == null) { return ResponseDTO.error( ResponseDTO.ERROR_CODE, "印模不存在:" + createDTO.getImpressionId() ); } CertificateEntity certificate = certificateMapper.selectById(createDTO.getCertId()); if (certificate == null) { return ResponseDTO.error( ResponseDTO.ERROR_CODE, "证书不存在:" + createDTO.getCertId() ); } // 4. 组装印章基础信息 Seal seal = new Seal(); seal.setSealAlias(createDTO.getSealAlias()); seal.setImpressionId(createDTO.getImpressionId()); seal.setImpressionAlias(picture.getName()); seal.setCertId(createDTO.getCertId()); seal.setCertAlias(certificate.getCertName()); seal.setAlgorithm(algorithm.getCode()); seal.setSealStatus(SealStatusEnum.NORMAL.getCode()); if (certificate.getValidFrom() == null || certificate.getValidTo() == null) { return ResponseDTO.error(ResponseDTO.ERROR_CODE, "证书有效期信息不完整"); } seal.setValidStartTime(certificate.getValidFrom()); seal.setValidEndTime(certificate.getValidTo()); seal.setRemark(createDTO.getRemark()); seal.setCreateBy(getCurrentUser()); seal.setUpdateBy(getCurrentUser()); LocalDateTime now = LocalDateTime.now(); seal.setCreateTime(now); seal.setUpdateTime(now); // 4. 按算法分流构建印章结构体 if (SealAlgorithmEnum.SM2 == algorithm) { seal.setSealFormat(SealFormatEnum.ESL.getCode()); SealBuildResult buildResult = sealBuilder.buildEsl(seal); seal.setSealFileData(buildResult.getFileData()); seal.setSealFilePath(buildResult.getFilePath()); } else { seal.setSealFormat(SealFormatEnum.CMS.getCode()); } // 5. 计算印章校验值 seal.setSealCheckValue(sealVerifyService.calcCheckValue(seal)); // 6. 保存 sealMapper.insert(seal); return ResponseDTO.ok(seal.getId()); } private String getCurrentUser() { // TODO 从 SecurityContext / ThreadLocal 获取当前登录用户 return "system"; } @Override @Transactional(rollbackFor = Exception.class) public ResponseDTO freeze(Long id) { Seal seal = sealMapper.selectById(id); if (seal == null) { return ResponseDTO.error(ResponseDTO.ERROR_CODE, "印章不存在"); } // 只有正常状态才允许冻结 if (!SealStatusEnum.NORMAL.getCode().equals(seal.getSealStatus())) { SealStatusEnum current = SealStatusEnum.of(seal.getSealStatus()); return ResponseDTO.error(ResponseDTO.ERROR_CODE, "当前状态不允许冻结:" + (current != null ? current.getDesc() : "未知")); } Seal update = new Seal(); update.setId(id); update.setSealStatus(SealStatusEnum.FROZEN.getCode()); update.setUpdateBy(getCurrentUser()); sealMapper.updateById(update); return ResponseDTO.ok(); } @Override @Transactional(rollbackFor = Exception.class) public ResponseDTO unfreeze(Long id) { Seal seal = sealMapper.selectById(id); if (seal == null) { return ResponseDTO.error(ResponseDTO.ERROR_CODE,"印章不存在"); } // 只有冻结状态才允许解冻 if (!SealStatusEnum.FROZEN.getCode().equals(seal.getSealStatus())) { SealStatusEnum current = SealStatusEnum.of(seal.getSealStatus()); return ResponseDTO.error(ResponseDTO.ERROR_CODE,"当前状态不允许解冻:" + (current != null ? current.getDesc() : "未知")); } Seal update = new Seal(); update.setId(id); update.setSealStatus(SealStatusEnum.NORMAL.getCode()); update.setUpdateBy(getCurrentUser()); sealMapper.updateById(update); return ResponseDTO.ok(); } @Override @Transactional(rollbackFor = Exception.class) public ResponseDTO destroy(Long id) { Seal seal = sealMapper.selectById(id); if (seal == null) { return ResponseDTO.error(ResponseDTO.ERROR_CODE,"印章不存在"); } // 已销毁的不允许重复销毁 if (SealStatusEnum.DESTROYED.getCode().equals(seal.getSealStatus())) { return ResponseDTO.error(ResponseDTO.ERROR_CODE,"印章已销毁,不可重复操作"); } Seal update = new Seal(); update.setId(id); update.setSealStatus(SealStatusEnum.DESTROYED.getCode()); update.setUpdateBy(getCurrentUser()); sealMapper.updateById(update); return ResponseDTO.ok(); } @Override @Transactional(rollbackFor = Exception.class) public ResponseDTO extend(SealExtendDTO extendDTO) { Seal seal = sealMapper.selectById(extendDTO.getSealId()); if (seal == null) { return ResponseDTO.error(ResponseDTO.ERROR_CODE,"印章不存在"); } if (!SealStatusEnum.NORMAL.getCode().equals(seal.getSealStatus())) { SealStatusEnum current = SealStatusEnum.of(seal.getSealStatus()); return ResponseDTO.error(ResponseDTO.ERROR_CODE,"当前状态不允许延期:" + (current != null ? current.getDesc() : "未知")); } if (seal.getValidEndTime() != null && !extendDTO.getNewValidEndTime().isAfter(seal.getValidEndTime())) { return ResponseDTO.error(ResponseDTO.ERROR_CODE,"延期时间必须晚于原有效期"); } Seal update = new Seal(); update.setId(seal.getId()); update.setValidEndTime(extendDTO.getNewValidEndTime()); // 用于重算校验值的对象 Seal checkSeal = new Seal(); checkSeal.setSealAlias(seal.getSealAlias()); checkSeal.setCertId(seal.getCertId()); checkSeal.setImpressionId(seal.getImpressionId()); checkSeal.setAlgorithm(seal.getAlgorithm()); checkSeal.setValidEndTime(extendDTO.getNewValidEndTime()); if (SealAlgorithmEnum.SM2.getCode().equals(seal.getAlgorithm())) { // SM2:重新生成 .esl seal.setValidEndTime(extendDTO.getNewValidEndTime()); SealBuildResult buildResult = sealBuilder.rebuildEsl(seal); update.setSealFileData(buildResult.getFileData()); update.setSealFilePath(buildResult.getFilePath()); checkSeal.setSealFileData(buildResult.getFileData()); } // RSA:无需重建,checkSeal.sealFileData 为 null,走元数据摘要 update.setSealCheckValue(sealVerifyService.calcCheckValue(checkSeal)); update.setUpdateBy(getCurrentUser()); sealMapper.updateById(update); return ResponseDTO.ok(); } @Override @Transactional(rollbackFor = Exception.class) public ResponseDTO bindCert(SealBindCertDTO bindCertDTO) { Seal seal = sealMapper.selectById(bindCertDTO.getSealId()); if (seal == null) { return ResponseDTO.error(ResponseDTO.ERROR_CODE,"印章不存在"); } if (!SealStatusEnum.NORMAL.getCode().equals(seal.getSealStatus())) { SealStatusEnum current = SealStatusEnum.of(seal.getSealStatus()); return ResponseDTO.error(ResponseDTO.ERROR_CODE,"当前状态不允许绑定证书:" + (current != null ? current.getDesc() : "未知")); } CertificateEntity certificate = certificateMapper.selectById(bindCertDTO.getCertId()); if (certificate == null) { return ResponseDTO.error(ResponseDTO.ERROR_CODE, "证书不存在:" + bindCertDTO.getCertId()); } /* Long boundSealCount = sealMapper.selectCount(new LambdaQueryWrapper() .eq(Seal::getCertId, bindCertDTO.getCertId()) .ne(Seal::getId, seal.getId())); if (boundSealCount != null && boundSealCount > 0) { return ResponseDTO.error(ResponseDTO.ERROR_CODE, "该证书已绑定其他印章"); }*/ Seal update = new Seal(); update.setId(seal.getId()); update.setCertId(bindCertDTO.getCertId()); update.setCertAlias(certificate.getCertName()); if (certificate.getValidFrom() == null || certificate.getValidTo() == null) { return ResponseDTO.error(ResponseDTO.ERROR_CODE, "证书有效期信息不完整"); } update.setValidStartTime(certificate.getValidFrom()); update.setValidEndTime(certificate.getValidTo()); // 用于重算校验值的对象 Seal checkSeal = new Seal(); checkSeal.setSealAlias(seal.getSealAlias()); checkSeal.setCertId(bindCertDTO.getCertId()); // 新证书 checkSeal.setImpressionId(seal.getImpressionId()); checkSeal.setAlgorithm(seal.getAlgorithm()); checkSeal.setValidEndTime(certificate.getValidTo()); if (SealAlgorithmEnum.SM2.getCode().equals(seal.getAlgorithm())) { // SM2:证书在 .esl 结构体内,必须重新生成 seal.setCertId(bindCertDTO.getCertId()); seal.setCertAlias(certificate.getCertName()); seal.setValidStartTime(certificate.getValidFrom()); seal.setValidEndTime(certificate.getValidTo()); SealBuildResult buildResult = sealBuilder.rebuildEsl(seal); update.setSealFileData(buildResult.getFileData()); update.setSealFilePath(buildResult.getFilePath()); checkSeal.setSealFileData(buildResult.getFileData()); } // RSA:只改 cert_id,无需重建 update.setSealCheckValue(sealVerifyService.calcCheckValue(checkSeal)); update.setUpdateBy(getCurrentUser()); sealMapper.updateById(update); return ResponseDTO.ok(); } @Override public ResponseDTO verify(Long id) { Seal seal = sealMapper.selectById(id); if (seal == null) { return ResponseDTO.error(ResponseDTO.ERROR_CODE,"印章不存在"); } boolean valid = sealVerifyService.verify(seal); return ResponseDTO.ok(valid); } @Override public ResponseDTO download(Long id) { Seal seal = sealMapper.selectById(id); if (seal == null) { return ResponseDTO.error(ResponseDTO.ERROR_CODE,"印章不存在"); } if (SealStatusEnum.DESTROYED.getCode().equals(seal.getSealStatus())) { return ResponseDTO.error(ResponseDTO.ERROR_CODE,"印章已销毁,不可下载"); } // SM2:返回 .esl 字节 if (SealAlgorithmEnum.SM2.getCode().equals(seal.getAlgorithm())) { byte[] data = seal.getSealFileData(); if (data == null || data.length == 0) { return ResponseDTO.error(ResponseDTO.ERROR_CODE,"印章文件不存在"); } return ResponseDTO.ok(new SealDownload( data, "seal.esl", "application/octet-stream")); } if (SealAlgorithmEnum.RSA.getCode().equals(seal.getAlgorithm())) { return downloadRsaSeal(seal); } return ResponseDTO.error(ResponseDTO.ERROR_CODE, "不支持的印章算法:" + seal.getAlgorithm()); } private ResponseDTO downloadRsaSeal(Seal seal) { Picture picture = pictureMapper.selectById(seal.getImpressionId()); if (picture == null || !StringUtils.hasText(picture.getUrl())) { return ResponseDTO.error(ResponseDTO.ERROR_CODE, "印章关联的印模文件不存在"); } CertificateEntity certificate = certificateMapper.selectById(seal.getCertId()); if (certificate == null || !StringUtils.hasText(certificate.getCertFilePath())) { return ResponseDTO.error(ResponseDTO.ERROR_CODE, "印章关联的证书文件不存在"); } String pictureObjectName = extractObjectName(picture.getUrl()); try (InputStream pictureStream = minioClientService.downloadFile(pictureObjectName); InputStream certificateStream = minioClientService.downloadFile(certificate.getCertFilePath()); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream)) { addZipEntry(zipOutputStream, "impression.png", pictureStream.readAllBytes()); addZipEntry(zipOutputStream, "certificate.pem", certificateStream.readAllBytes()); zipOutputStream.finish(); return ResponseDTO.ok(new SealDownload( outputStream.toByteArray(), "seal_" + seal.getId() + ".zip", "application/zip")); } catch (IOException e) { throw new RuntimeException("打包RSA印章下载文件失败", e); } } private String extractObjectName(String url) { String objectName = url.substring(url.lastIndexOf('/') + 1); int queryIndex = objectName.indexOf('?'); return queryIndex >= 0 ? objectName.substring(0, queryIndex) : objectName; } private void addZipEntry(ZipOutputStream zipOutputStream, String fileName, byte[] content) throws java.io.IOException { zipOutputStream.putNextEntry(new ZipEntry(fileName)); zipOutputStream.write(content); zipOutputStream.closeEntry(); } }