SealServiceImpl.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. package com.ymwk.icvp.signature.service.impl;
  2. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  3. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  4. import com.ymwk.icvp.component.PageResult;
  5. import com.ymwk.icvp.component.ResponseDTO;
  6. import com.ymwk.icvp.pki.entity.CertificateEntity;
  7. import com.ymwk.icvp.pki.mapper.CertificateMapper;
  8. import com.ymwk.icvp.signature.entity.Picture;
  9. import com.ymwk.icvp.signature.entity.Seal;
  10. import com.ymwk.icvp.signature.mapper.PictureMapper;
  11. import com.ymwk.icvp.signature.mapper.SealMapper;
  12. import com.ymwk.icvp.signature.model.*;
  13. import com.ymwk.icvp.signature.service.SealBuilder;
  14. import com.ymwk.icvp.signature.service.SealService;
  15. import com.ymwk.icvp.signature.service.SealVerifyService;
  16. import com.ymwk.icvp.utils.MinioClientService;
  17. import lombok.RequiredArgsConstructor;
  18. import org.springframework.stereotype.Service;
  19. import org.springframework.transaction.annotation.Transactional;
  20. import org.springframework.util.StringUtils;
  21. import java.io.ByteArrayOutputStream;
  22. import java.io.IOException;
  23. import java.io.InputStream;
  24. import java.time.LocalDateTime;
  25. import java.util.zip.ZipEntry;
  26. import java.util.zip.ZipOutputStream;
  27. @Service
  28. @RequiredArgsConstructor
  29. public class SealServiceImpl implements SealService {
  30. private final SealMapper sealMapper;
  31. private final SealBuilder sealBuilder;
  32. private final SealVerifyService sealVerifyService;
  33. private final PictureMapper pictureMapper;
  34. private final CertificateMapper certificateMapper;
  35. private final MinioClientService minioClientService;
  36. @Override
  37. public ResponseDTO<PageResult<Seal>> pageSeal(SealQueryDTO queryDTO) {
  38. LambdaQueryWrapper<Seal> wrapper = new LambdaQueryWrapper<>();
  39. wrapper.like(StringUtils.hasText(queryDTO.getSealAlias()),
  40. Seal::getSealAlias, queryDTO.getSealAlias())
  41. .eq(queryDTO.getSealStatus() != null,
  42. Seal::getSealStatus, queryDTO.getSealStatus())
  43. .eq(StringUtils.hasText(queryDTO.getAlgorithm()),
  44. Seal::getAlgorithm, queryDTO.getAlgorithm())
  45. .ge(queryDTO.getCreateTimeStart() != null,
  46. Seal::getCreateTime, queryDTO.getCreateTimeStart())
  47. .le(queryDTO.getCreateTimeEnd() != null,
  48. Seal::getCreateTime, queryDTO.getCreateTimeEnd())
  49. .orderByDesc(Seal::getCreateTime);
  50. Page<Seal> page = sealMapper.selectPage(
  51. new Page<>(queryDTO.getPageNum(), queryDTO.getPageSize()), wrapper);
  52. PageResult<Seal> result = new PageResult<>();
  53. result.setTotal(page.getTotal());
  54. result.setList(page.getRecords());
  55. return ResponseDTO.ok(result);
  56. }
  57. @Override
  58. public ResponseDTO<Seal> getSealDetail(Long id) {
  59. Seal seal = sealMapper.selectById(id);
  60. /* if (seal == null) {
  61. return ResponseDTO.error(100001,"印章不存在");
  62. }*/
  63. return ResponseDTO.ok(seal);
  64. }
  65. @Override
  66. @Transactional(rollbackFor = Exception.class)
  67. public ResponseDTO<Long> createSeal(SealCreateDTO createDTO) {
  68. // 1. 校验印章别名唯一
  69. Long count = sealMapper.selectCount(new LambdaQueryWrapper<Seal>()
  70. .eq(Seal::getSealAlias, createDTO.getSealAlias()));
  71. if (count > 0) {
  72. return ResponseDTO.error(ResponseDTO.ERROR_CODE, "印章别名已存在:" + createDTO.getSealAlias());
  73. }
  74. // 2. 校验算法
  75. SealAlgorithmEnum algorithm = SealAlgorithmEnum.of(createDTO.getAlgorithm());
  76. if (algorithm == null) {
  77. return ResponseDTO.error(ResponseDTO.ERROR_CODE, "不支持的印章算法:" + createDTO.getAlgorithm());
  78. }
  79. // 3. 查询印模和证书
  80. Picture picture = pictureMapper.selectById(createDTO.getImpressionId());
  81. if (picture == null) {
  82. return ResponseDTO.error(
  83. ResponseDTO.ERROR_CODE,
  84. "印模不存在:" + createDTO.getImpressionId()
  85. );
  86. }
  87. CertificateEntity certificate = certificateMapper.selectById(createDTO.getCertId());
  88. if (certificate == null) {
  89. return ResponseDTO.error(
  90. ResponseDTO.ERROR_CODE,
  91. "证书不存在:" + createDTO.getCertId()
  92. );
  93. }
  94. // 4. 组装印章基础信息
  95. Seal seal = new Seal();
  96. seal.setSealAlias(createDTO.getSealAlias());
  97. seal.setImpressionId(createDTO.getImpressionId());
  98. seal.setImpressionAlias(picture.getName());
  99. seal.setCertId(createDTO.getCertId());
  100. seal.setCertAlias(certificate.getCertName());
  101. seal.setAlgorithm(algorithm.getCode());
  102. seal.setSealStatus(SealStatusEnum.NORMAL.getCode());
  103. if (certificate.getValidFrom() == null || certificate.getValidTo() == null) {
  104. return ResponseDTO.error(ResponseDTO.ERROR_CODE, "证书有效期信息不完整");
  105. }
  106. seal.setValidStartTime(certificate.getValidFrom());
  107. seal.setValidEndTime(certificate.getValidTo());
  108. seal.setRemark(createDTO.getRemark());
  109. seal.setCreateBy(getCurrentUser());
  110. seal.setUpdateBy(getCurrentUser());
  111. LocalDateTime now = LocalDateTime.now();
  112. seal.setCreateTime(now);
  113. seal.setUpdateTime(now);
  114. // 4. 按算法分流构建印章结构体
  115. if (SealAlgorithmEnum.SM2 == algorithm) {
  116. seal.setSealFormat(SealFormatEnum.ESL.getCode());
  117. SealBuildResult buildResult = sealBuilder.buildEsl(seal);
  118. seal.setSealFileData(buildResult.getFileData());
  119. seal.setSealFilePath(buildResult.getFilePath());
  120. } else {
  121. seal.setSealFormat(SealFormatEnum.CMS.getCode());
  122. }
  123. // 5. 计算印章校验值
  124. seal.setSealCheckValue(sealVerifyService.calcCheckValue(seal));
  125. // 6. 保存
  126. sealMapper.insert(seal);
  127. return ResponseDTO.ok(seal.getId());
  128. }
  129. private String getCurrentUser() {
  130. // TODO 从 SecurityContext / ThreadLocal 获取当前登录用户
  131. return "system";
  132. }
  133. @Override
  134. @Transactional(rollbackFor = Exception.class)
  135. public ResponseDTO<Void> freeze(Long id) {
  136. Seal seal = sealMapper.selectById(id);
  137. if (seal == null) {
  138. return ResponseDTO.error(ResponseDTO.ERROR_CODE, "印章不存在");
  139. }
  140. // 只有正常状态才允许冻结
  141. if (!SealStatusEnum.NORMAL.getCode().equals(seal.getSealStatus())) {
  142. SealStatusEnum current = SealStatusEnum.of(seal.getSealStatus());
  143. return ResponseDTO.error(ResponseDTO.ERROR_CODE, "当前状态不允许冻结:" + (current != null ? current.getDesc() : "未知"));
  144. }
  145. Seal update = new Seal();
  146. update.setId(id);
  147. update.setSealStatus(SealStatusEnum.FROZEN.getCode());
  148. update.setUpdateBy(getCurrentUser());
  149. sealMapper.updateById(update);
  150. return ResponseDTO.ok();
  151. }
  152. @Override
  153. @Transactional(rollbackFor = Exception.class)
  154. public ResponseDTO<Void> unfreeze(Long id) {
  155. Seal seal = sealMapper.selectById(id);
  156. if (seal == null) {
  157. return ResponseDTO.error(ResponseDTO.ERROR_CODE,"印章不存在");
  158. }
  159. // 只有冻结状态才允许解冻
  160. if (!SealStatusEnum.FROZEN.getCode().equals(seal.getSealStatus())) {
  161. SealStatusEnum current = SealStatusEnum.of(seal.getSealStatus());
  162. return ResponseDTO.error(ResponseDTO.ERROR_CODE,"当前状态不允许解冻:" + (current != null ? current.getDesc() : "未知"));
  163. }
  164. Seal update = new Seal();
  165. update.setId(id);
  166. update.setSealStatus(SealStatusEnum.NORMAL.getCode());
  167. update.setUpdateBy(getCurrentUser());
  168. sealMapper.updateById(update);
  169. return ResponseDTO.ok();
  170. }
  171. @Override
  172. @Transactional(rollbackFor = Exception.class)
  173. public ResponseDTO<Void> destroy(Long id) {
  174. Seal seal = sealMapper.selectById(id);
  175. if (seal == null) {
  176. return ResponseDTO.error(ResponseDTO.ERROR_CODE,"印章不存在");
  177. }
  178. // 已销毁的不允许重复销毁
  179. if (SealStatusEnum.DESTROYED.getCode().equals(seal.getSealStatus())) {
  180. return ResponseDTO.error(ResponseDTO.ERROR_CODE,"印章已销毁,不可重复操作");
  181. }
  182. Seal update = new Seal();
  183. update.setId(id);
  184. update.setSealStatus(SealStatusEnum.DESTROYED.getCode());
  185. update.setUpdateBy(getCurrentUser());
  186. sealMapper.updateById(update);
  187. return ResponseDTO.ok();
  188. }
  189. @Override
  190. @Transactional(rollbackFor = Exception.class)
  191. public ResponseDTO<Void> extend(SealExtendDTO extendDTO) {
  192. Seal seal = sealMapper.selectById(extendDTO.getSealId());
  193. if (seal == null) {
  194. return ResponseDTO.error(ResponseDTO.ERROR_CODE,"印章不存在");
  195. }
  196. if (!SealStatusEnum.NORMAL.getCode().equals(seal.getSealStatus())) {
  197. SealStatusEnum current = SealStatusEnum.of(seal.getSealStatus());
  198. return ResponseDTO.error(ResponseDTO.ERROR_CODE,"当前状态不允许延期:" + (current != null ? current.getDesc() : "未知"));
  199. }
  200. if (seal.getValidEndTime() != null
  201. && !extendDTO.getNewValidEndTime().isAfter(seal.getValidEndTime())) {
  202. return ResponseDTO.error(ResponseDTO.ERROR_CODE,"延期时间必须晚于原有效期");
  203. }
  204. Seal update = new Seal();
  205. update.setId(seal.getId());
  206. update.setValidEndTime(extendDTO.getNewValidEndTime());
  207. // 用于重算校验值的对象
  208. Seal checkSeal = new Seal();
  209. checkSeal.setSealAlias(seal.getSealAlias());
  210. checkSeal.setCertId(seal.getCertId());
  211. checkSeal.setImpressionId(seal.getImpressionId());
  212. checkSeal.setAlgorithm(seal.getAlgorithm());
  213. checkSeal.setValidEndTime(extendDTO.getNewValidEndTime());
  214. if (SealAlgorithmEnum.SM2.getCode().equals(seal.getAlgorithm())) {
  215. // SM2:重新生成 .esl
  216. seal.setValidEndTime(extendDTO.getNewValidEndTime());
  217. SealBuildResult buildResult = sealBuilder.rebuildEsl(seal);
  218. update.setSealFileData(buildResult.getFileData());
  219. update.setSealFilePath(buildResult.getFilePath());
  220. checkSeal.setSealFileData(buildResult.getFileData());
  221. }
  222. // RSA:无需重建,checkSeal.sealFileData 为 null,走元数据摘要
  223. update.setSealCheckValue(sealVerifyService.calcCheckValue(checkSeal));
  224. update.setUpdateBy(getCurrentUser());
  225. sealMapper.updateById(update);
  226. return ResponseDTO.ok();
  227. }
  228. @Override
  229. @Transactional(rollbackFor = Exception.class)
  230. public ResponseDTO<Void> bindCert(SealBindCertDTO bindCertDTO) {
  231. Seal seal = sealMapper.selectById(bindCertDTO.getSealId());
  232. if (seal == null) {
  233. return ResponseDTO.error(ResponseDTO.ERROR_CODE,"印章不存在");
  234. }
  235. if (!SealStatusEnum.NORMAL.getCode().equals(seal.getSealStatus())) {
  236. SealStatusEnum current = SealStatusEnum.of(seal.getSealStatus());
  237. return ResponseDTO.error(ResponseDTO.ERROR_CODE,"当前状态不允许绑定证书:" + (current != null ? current.getDesc() : "未知"));
  238. }
  239. CertificateEntity certificate = certificateMapper.selectById(bindCertDTO.getCertId());
  240. if (certificate == null) {
  241. return ResponseDTO.error(ResponseDTO.ERROR_CODE, "证书不存在:" + bindCertDTO.getCertId());
  242. }
  243. /* Long boundSealCount = sealMapper.selectCount(new LambdaQueryWrapper<Seal>()
  244. .eq(Seal::getCertId, bindCertDTO.getCertId())
  245. .ne(Seal::getId, seal.getId()));
  246. if (boundSealCount != null && boundSealCount > 0) {
  247. return ResponseDTO.error(ResponseDTO.ERROR_CODE, "该证书已绑定其他印章");
  248. }*/
  249. Seal update = new Seal();
  250. update.setId(seal.getId());
  251. update.setCertId(bindCertDTO.getCertId());
  252. update.setCertAlias(certificate.getCertName());
  253. if (certificate.getValidFrom() == null || certificate.getValidTo() == null) {
  254. return ResponseDTO.error(ResponseDTO.ERROR_CODE, "证书有效期信息不完整");
  255. }
  256. update.setValidStartTime(certificate.getValidFrom());
  257. update.setValidEndTime(certificate.getValidTo());
  258. // 用于重算校验值的对象
  259. Seal checkSeal = new Seal();
  260. checkSeal.setSealAlias(seal.getSealAlias());
  261. checkSeal.setCertId(bindCertDTO.getCertId()); // 新证书
  262. checkSeal.setImpressionId(seal.getImpressionId());
  263. checkSeal.setAlgorithm(seal.getAlgorithm());
  264. checkSeal.setValidEndTime(certificate.getValidTo());
  265. if (SealAlgorithmEnum.SM2.getCode().equals(seal.getAlgorithm())) {
  266. // SM2:证书在 .esl 结构体内,必须重新生成
  267. seal.setCertId(bindCertDTO.getCertId());
  268. seal.setCertAlias(certificate.getCertName());
  269. seal.setValidStartTime(certificate.getValidFrom());
  270. seal.setValidEndTime(certificate.getValidTo());
  271. SealBuildResult buildResult = sealBuilder.rebuildEsl(seal);
  272. update.setSealFileData(buildResult.getFileData());
  273. update.setSealFilePath(buildResult.getFilePath());
  274. checkSeal.setSealFileData(buildResult.getFileData());
  275. }
  276. // RSA:只改 cert_id,无需重建
  277. update.setSealCheckValue(sealVerifyService.calcCheckValue(checkSeal));
  278. update.setUpdateBy(getCurrentUser());
  279. sealMapper.updateById(update);
  280. return ResponseDTO.ok();
  281. }
  282. @Override
  283. public ResponseDTO<Boolean> verify(Long id) {
  284. Seal seal = sealMapper.selectById(id);
  285. if (seal == null) {
  286. return ResponseDTO.error(ResponseDTO.ERROR_CODE,"印章不存在");
  287. }
  288. boolean valid = sealVerifyService.verify(seal);
  289. return ResponseDTO.ok(valid);
  290. }
  291. @Override
  292. public ResponseDTO<SealDownload> download(Long id) {
  293. Seal seal = sealMapper.selectById(id);
  294. if (seal == null) {
  295. return ResponseDTO.error(ResponseDTO.ERROR_CODE,"印章不存在");
  296. }
  297. if (SealStatusEnum.DESTROYED.getCode().equals(seal.getSealStatus())) {
  298. return ResponseDTO.error(ResponseDTO.ERROR_CODE,"印章已销毁,不可下载");
  299. }
  300. // SM2:返回 .esl 字节
  301. if (SealAlgorithmEnum.SM2.getCode().equals(seal.getAlgorithm())) {
  302. byte[] data = seal.getSealFileData();
  303. if (data == null || data.length == 0) {
  304. return ResponseDTO.error(ResponseDTO.ERROR_CODE,"印章文件不存在");
  305. }
  306. return ResponseDTO.ok(new SealDownload(
  307. data, "seal.esl", "application/octet-stream"));
  308. }
  309. if (SealAlgorithmEnum.RSA.getCode().equals(seal.getAlgorithm())) {
  310. return downloadRsaSeal(seal);
  311. }
  312. return ResponseDTO.error(ResponseDTO.ERROR_CODE, "不支持的印章算法:" + seal.getAlgorithm());
  313. }
  314. private ResponseDTO<SealDownload> downloadRsaSeal(Seal seal) {
  315. Picture picture = pictureMapper.selectById(seal.getImpressionId());
  316. if (picture == null || !StringUtils.hasText(picture.getUrl())) {
  317. return ResponseDTO.error(ResponseDTO.ERROR_CODE, "印章关联的印模文件不存在");
  318. }
  319. CertificateEntity certificate = certificateMapper.selectById(seal.getCertId());
  320. if (certificate == null || !StringUtils.hasText(certificate.getCertFilePath())) {
  321. return ResponseDTO.error(ResponseDTO.ERROR_CODE, "印章关联的证书文件不存在");
  322. }
  323. String pictureObjectName = extractObjectName(picture.getUrl());
  324. try (InputStream pictureStream = minioClientService.downloadFile(pictureObjectName);
  325. InputStream certificateStream = minioClientService.downloadFile(certificate.getCertFilePath());
  326. ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
  327. ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream)) {
  328. addZipEntry(zipOutputStream, "impression.png", pictureStream.readAllBytes());
  329. addZipEntry(zipOutputStream, "certificate.pem", certificateStream.readAllBytes());
  330. zipOutputStream.finish();
  331. return ResponseDTO.ok(new SealDownload(
  332. outputStream.toByteArray(), "seal_" + seal.getId() + ".zip", "application/zip"));
  333. } catch (IOException e) {
  334. throw new RuntimeException("打包RSA印章下载文件失败", e);
  335. }
  336. }
  337. private String extractObjectName(String url) {
  338. String objectName = url.substring(url.lastIndexOf('/') + 1);
  339. int queryIndex = objectName.indexOf('?');
  340. return queryIndex >= 0 ? objectName.substring(0, queryIndex) : objectName;
  341. }
  342. private void addZipEntry(ZipOutputStream zipOutputStream, String fileName, byte[] content)
  343. throws java.io.IOException {
  344. zipOutputStream.putNextEntry(new ZipEntry(fileName));
  345. zipOutputStream.write(content);
  346. zipOutputStream.closeEntry();
  347. }
  348. }