package com.yau.digitalrmb.institutionidentity.application; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.yau.digitalrmb.institutionidentity.domain.InstitutionIdentityCryptography; import com.yau.digitalrmb.institutionidentity.infrastructure.ControlSystemSignatureEntity; import com.yau.digitalrmb.institutionidentity.infrastructure.ControlSystemSignatureMapper; import com.yau.digitalrmb.institutionidentity.infrastructure.TransactionInformationIdentifierEntity; import com.yau.digitalrmb.institutionidentity.infrastructure.TransactionInformationIdentifierMapper; import com.yau.digitalrmb.shared.api.ErrorCode; import com.yau.digitalrmb.shared.exception.BusinessException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.math.BigDecimal; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @Service public class ControlSystemSignatureService { private static final DateTimeFormatter DATE = DateTimeFormatter.ofPattern("yyyyMMdd"); @Resource private TransactionInformationIdentifierMapper transactionMapper; @Resource private ControlSystemSignatureMapper mapper; @Resource private InstitutionIdentityCryptography cryptography; @Resource private InstitutionKeyService keyService; @Resource private InstitutionTrainingScoreService scoreService; @Resource private InstitutionTrainingErrorRecorder errorRecorder; @Transactional public ControlSystemDigestResult digest(String originalText, InstitutionKeySubject subject, String operator) { TransactionInformationIdentifierEntity transaction = requireQuotaVerified(subject); String normalizedOriginalText = validateOriginalText(originalText, transaction, subject); ControlSystemSignatureEntity current = latest(subject); if (current != null) { if (!current.getTransactionIdentifierId().equals(transaction.getId())) { throw new BusinessException(ErrorCode.VALIDATION_ERROR, "当前实训已有步骤七控制系统签名记录,如需重新开始请先调用重新实训接口"); } String digest = cryptography.sm3(normalizedOriginalText); update(current, current.getStatus(), "DIGESTED", operator, new LambdaUpdateWrapper() .set(ControlSystemSignatureEntity::getOriginalText, normalizedOriginalText) .set(ControlSystemSignatureEntity::getDigest, digest) .set(ControlSystemSignatureEntity::getSigningKeyId, null) .set(ControlSystemSignatureEntity::getSignature, null) .set(ControlSystemSignatureEntity::getControlSignature, null) .set(ControlSystemSignatureEntity::getResponseMessage, null)); return new ControlSystemDigestResult(digest); } ControlSystemSignatureEntity value = new ControlSystemSignatureEntity(); value.setTransactionIdentifierId(transaction.getId()); value.setControlId("CTRL_" + LocalDateTime.now().format(DATE) + "_" + String.format("%03d", transaction.getId() % 1000)); value.setAmount(transaction.getAmount()); value.setInstitutionIdentifier(transaction.getInstitutionIdentifier()); value.setTransactionIdentifier(transaction.getTransactionIdentifier()); value.setOriginalText(normalizedOriginalText); value.setDigest(cryptography.sm3(normalizedOriginalText)); value.setStatus("DIGESTED"); value.setUserId(subject.getUserId()); value.setSchoolId(subject.getSchoolId()); value.setClassId(subject.getClassId()); value.setCreatedAt(LocalDateTime.now()); value.setUpdatedAt(value.getCreatedAt()); value.setCreatedBy(operator); value.setUpdatedBy(operator); value.setDeleted(false); mapper.insert(value); return new ControlSystemDigestResult(value.getDigest()); } @Transactional public ControlSystemSignatureResult sign(String privateKey, InstitutionKeySubject subject, String operator) { ControlSystemSignatureEntity value = requireLatest(subject); requireResult(value.getDigest(), "请先计算控制系统签名摘要"); String signature = keyService.signCentralBank(subject, privateKey, value.getDigest()); if (signature == null) { int errorSequence = errorRecorder.recordScoreError(subject.getUserId()); throw new BusinessException(ErrorCode.VALIDATION_ERROR, "中央银行第一私钥不正确,本次实训第" + errorSequence + "次错误"); } update(value, value.getStatus(), "SIGNED", operator, new LambdaUpdateWrapper() .set(ControlSystemSignatureEntity::getSigningKeyId, InstitutionKeyService.CENTRAL_FIRST_KEY) .set(ControlSystemSignatureEntity::getSignature, signature) .set(ControlSystemSignatureEntity::getControlSignature, signature) .set(ControlSystemSignatureEntity::getResponseMessage, null)); return new ControlSystemSignatureResult(requireLatest(subject)); } @Transactional public ControlSystemSignatureResult packageResponse(InstitutionKeySubject subject, String operator) { ControlSystemSignatureEntity value = requireLatest(subject); requireResult(value.getControlSignature(), "请先完成中央银行控制系统签名"); String message = "{\"ctrlId\":\"" + value.getControlId() + "\",\"amount\":\"" + value.getAmount().setScale(2).toPlainString() + "\",\"orgId\":\"" + value.getInstitutionIdentifier() + "\",\"txnId\":\"" + value.getTransactionIdentifier() + "\",\"cbCtrlSignature\":\"" + value.getControlSignature() + "\"}"; update(value, value.getStatus(), "PACKAGED", operator, new LambdaUpdateWrapper() .set(ControlSystemSignatureEntity::getResponseMessage, message)); return new ControlSystemSignatureResult(requireLatest(subject)); } @Transactional public ControlSystemSignatureResult send(InstitutionKeySubject subject, String operator) { ControlSystemSignatureEntity value = requireLatest(subject); requireResult(value.getResponseMessage(), "请先打包控制系统签名反馈报文"); update(value, value.getStatus(), "SENT", operator, new LambdaUpdateWrapper()); scoreService.markStepCompleted(subject.getUserId(), 7); return new ControlSystemSignatureResult(requireLatest(subject)); } @Transactional(readOnly = true) public ControlSystemSignatureResult detail(InstitutionKeySubject subject) { return new ControlSystemSignatureResult(requireLatest(subject)); } @Transactional(readOnly = true) public CommercialBankControlSystemSignatureResult commercialBankMessage(InstitutionKeySubject subject) { return new CommercialBankControlSystemSignatureResult(latest(subject)); } private String validateOriginalText(String originalText, TransactionInformationIdentifierEntity transaction, InstitutionKeySubject subject) { String value = originalText == null ? null : originalText.trim(); if (value == null || value.isEmpty()) { throw dataError(subject, "拼接请求原文不能为空"); } String[] parts = value.split("\\|", -1); if (parts.length != 3) { throw dataError(subject, "拼接请求原文格式必须为:金额|机构标识|交易信息标识"); } BigDecimal amount; try { amount = new BigDecimal(parts[0]); } catch (NumberFormatException exception) { throw dataError(subject, "拼接请求原文中的金额格式不正确"); } if (amount.compareTo(transaction.getAmount()) != 0 || !transaction.getInstitutionIdentifier().equals(parts[1]) || !transaction.getTransactionIdentifier().equals(parts[2])) { throw dataError(subject, "拼接请求原文中的金额、机构标识或交易信息标识与当前用户前序数据不一致"); } return value; } private BusinessException dataError(InstitutionKeySubject subject, String message) { int errorSequence = errorRecorder.recordScoreError(subject.getUserId()); return new BusinessException(ErrorCode.VALIDATION_ERROR, message + ",本次实训第" + errorSequence + "次错误"); } private TransactionInformationIdentifierEntity requireQuotaVerified(InstitutionKeySubject subject) { TransactionInformationIdentifierEntity value = transactionMapper.selectOne( new LambdaQueryWrapper() .eq(TransactionInformationIdentifierEntity::getUserId, subject.getUserId()) .eq(TransactionInformationIdentifierEntity::getSchoolId, subject.getSchoolId()) .eq(TransactionInformationIdentifierEntity::getClassId, subject.getClassId()) .eq(TransactionInformationIdentifierEntity::getStatus, "QUOTA_VERIFIED") .eq(TransactionInformationIdentifierEntity::getDeleted, false) .orderByDesc(TransactionInformationIdentifierEntity::getCreatedAt).last("LIMIT 1")); if (value == null) throw new BusinessException(ErrorCode.VALIDATION_ERROR, "步骤六尚未完成额度验证,不能进入步骤七"); return value; } private void requireResult(String value, String message) { if (value == null || value.trim().isEmpty()) { throw new BusinessException(ErrorCode.VALIDATION_ERROR, message); } } private ControlSystemSignatureEntity requireStatus(InstitutionKeySubject subject, String status, String message) { ControlSystemSignatureEntity value = requireLatest(subject); if (!status.equals(value.getStatus())) { throw new BusinessException(ErrorCode.VALIDATION_ERROR, message + ",当前状态:" + value.getStatus()); } return value; } private ControlSystemSignatureEntity requireLatest(InstitutionKeySubject subject) { ControlSystemSignatureEntity value = latest(subject); if (value == null) throw new BusinessException(ErrorCode.RESOURCE_NOT_FOUND, "当前没有步骤七控制系统签名记录"); return value; } private ControlSystemSignatureEntity latest(InstitutionKeySubject subject) { return mapper.selectOne(new LambdaQueryWrapper() .eq(ControlSystemSignatureEntity::getUserId, subject.getUserId()) .eq(ControlSystemSignatureEntity::getSchoolId, subject.getSchoolId()) .eq(ControlSystemSignatureEntity::getClassId, subject.getClassId()) .eq(ControlSystemSignatureEntity::getDeleted, false) .orderByDesc(ControlSystemSignatureEntity::getCreatedAt).last("LIMIT 1")); } private void update(ControlSystemSignatureEntity value, String from, String to, String operator, LambdaUpdateWrapper fields) { fields.eq(ControlSystemSignatureEntity::getId, value.getId()) .eq(ControlSystemSignatureEntity::getStatus, from) .eq(ControlSystemSignatureEntity::getDeleted, false) .set(ControlSystemSignatureEntity::getStatus, to) .set(ControlSystemSignatureEntity::getUpdatedAt, LocalDateTime.now()) .set(ControlSystemSignatureEntity::getUpdatedBy, operator); if (mapper.update(null, fields) != 1) { throw new BusinessException(ErrorCode.VALIDATION_ERROR, "步骤七状态已变化,不能重复或越级操作"); } } }