You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
digital-rmb-backend/src/main/java/com/yau/digitalrmb/institutionidentity/application/InstitutionIdentifierStepSe...

125 lines
6.4 KiB
Java

package com.yau.digitalrmb.institutionidentity.application;
import com.yau.digitalrmb.institutionidentity.domain.InstitutionIdentifierApplication;
import com.yau.digitalrmb.institutionidentity.domain.InstitutionIdentifierRepository;
import com.yau.digitalrmb.institutionidentity.domain.InstitutionIdentityCryptography;
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.time.Clock;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
@Service
public class InstitutionIdentifierStepService {
private static final DateTimeFormatter TIMESTAMP = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
@Resource
private InstitutionIdentityCryptography cryptography;
@Resource
private InstitutionKeyService keyService;
@Resource
private InstitutionIdentifierRepository repository;
@Resource
private InstitutionTrainingErrorRecorder errorRecorder;
private final Clock clock = Clock.systemUTC();
@Transactional
public InstitutionInformationPreparationResult prepare(String bankCode, String operator) {
String normalized = normalize(bankCode);
int trainingRound = repository.nextTrainingRound(normalized, operator);
String timestamp = LocalDateTime.ofInstant(clock.instant(), ZoneOffset.UTC).format(TIMESTAMP);
InstitutionIdentifierApplication application = InstitutionIdentifierApplication.prepare(normalized, timestamp);
long applicationId = repository.save(application, trainingRound, operator);
return new InstitutionInformationPreparationResult(applicationId, application.getStatus().name(), normalized,
timestamp, normalized + "|" + timestamp, trainingRound, 0);
}
@Transactional
public InstitutionDigestResult digest(long applicationId, String operator) {
InstitutionIdentifierApplication application = requireApplication(applicationId);
requireStatus(application, InstitutionIdentifierApplication.Status.PREPARED, "当前步骤不是摘要计算,不能操作", operator);
InstitutionIdentifierApplication.Status expectedStatus = application.getStatus();
String text = application.getBankCode() + "|" + application.getTimestamp();
String digest = cryptography.sm3(text);
invokeDomain(() -> application.recordDigest(digest));
repository.update(application, expectedStatus, "DIGEST", "使用SM3计算并保存业务摘要", operator);
return new InstitutionDigestResult(applicationId, application.getStatus().name(), "SM3", text, digest);
}
@Transactional
public InstitutionSignatureResult sign(long applicationId, String keyId, InstitutionKeySubject subject,
String operator) {
InstitutionIdentifierApplication application = requireApplication(applicationId);
if (!InstitutionKeyService.BANK_SECOND_KEY.equals(keyId)) {
recordAndThrow(application, "WRONG_KEY", "SIGN", keyId,
"签名必须选择商业银行第二密钥", operator);
}
requireStatus(application, InstitutionIdentifierApplication.Status.DIGESTED,
"当前步骤不是商业银行签名,不能操作", operator);
InstitutionIdentifierApplication.Status expectedStatus = application.getStatus();
String signature = keyService.signCommercialBank(subject, application.getDigest());
invokeDomain(() -> application.recordBankSignature(signature));
repository.update(application, expectedStatus, "SIGN", "使用商业银行第二私钥完成SM2签名", operator);
return new InstitutionSignatureResult(applicationId, application.getStatus().name(), "SM2", keyId,
"COMMERCIAL_BANK_SECOND_PRIVATE_KEY", signature);
}
@Transactional
public InstitutionApplicationPackageResult packageApplication(long applicationId, String operator) {
InstitutionIdentifierApplication application = requireApplication(applicationId);
requireStatus(application, InstitutionIdentifierApplication.Status.SIGNED, "当前步骤不是申请组装,不能操作", operator);
InstitutionIdentifierApplication.Status expectedStatus = application.getStatus();
invokeDomain(application::packageApplication);
repository.update(application, expectedStatus, "PACKAGE", "组装机构标识申请材料并提交央行待验证", operator);
return new InstitutionApplicationPackageResult(applicationId, application.getStatus().name(),
application.getBankCode(), application.getTimestamp(), application.getDigest(),
application.getBankSignature());
}
private InstitutionIdentifierApplication requireApplication(long applicationId) {
return repository.findById(applicationId).orElseThrow(() ->
new BusinessException(ErrorCode.RESOURCE_NOT_FOUND, "机构标识申请不存在"));
}
private void requireStatus(InstitutionIdentifierApplication application,
InstitutionIdentifierApplication.Status expectedStatus,
String message, String operator) {
if (application.getStatus() != expectedStatus) {
recordAndThrow(application, "INVALID_STEP", expectedStatus.name(), null,
message + ",当前状态:" + application.getStatus().name(), operator);
}
}
private void recordAndThrow(InstitutionIdentifierApplication application, String errorType, String operationStep,
String providedKeyId, String message, String operator) {
int errorSequence = errorRecorder.record(application.getId(), errorType, operationStep, providedKeyId,
message, operator);
invalid(message + ",本次实训第" + errorSequence + "次错误");
}
private static void invokeDomain(Runnable operation) {
try {
operation.run();
} catch (IllegalArgumentException | IllegalStateException exception) {
invalid(exception.getMessage());
}
}
private static String normalize(String value) {
return value.trim().toUpperCase();
}
private static void invalid(String message) {
throw new BusinessException(ErrorCode.VALIDATION_ERROR, message);
}
}