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/CurrencyGenerationRequestSe...

201 lines
11 KiB
Java

package com.yau.digitalrmb.institutionidentity.application;
import com.yau.digitalrmb.institutionidentity.domain.CurrencyGenerationRequest;
import com.yau.digitalrmb.institutionidentity.domain.CurrencyGenerationRequestRepository;
import com.yau.digitalrmb.institutionidentity.application.InstitutionKeyService;
import com.yau.digitalrmb.institutionidentity.application.InstitutionKeySubject;
import com.yau.digitalrmb.institutionidentity.application.IssuedInstitutionIdentifierQueryService;
import com.yau.digitalrmb.institutionidentity.application.IssuedInstitutionIdentifierResult;
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.math.BigDecimal;
@Service
public class CurrencyGenerationRequestService {
@Resource
private CurrencyGenerationRequestRepository repository;
@Resource
private IssuedInstitutionIdentifierQueryService identifierQueryService;
@Resource
private InstitutionIdentityCryptography cryptography;
@Resource
private InstitutionKeyService keyService;
@Resource
private InstitutionTrainingScoreService scoreService;
@Resource
private InstitutionTrainingErrorRecorder errorRecorder;
@Transactional
public CurrencyDigestResult generateDigest(BigDecimal amount, String deliveryNodeCode,
String institutionIdentifier, String requestTimestamp,
InstitutionKeySubject subject, String operator) {
IssuedInstitutionIdentifierResult identifier = identifierQueryService.requireLatestFeedbacked(subject);
if (!identifier.getInstitutionIdentifier().equals(institutionIdentifier)) {
throw new BusinessException(ErrorCode.VALIDATION_ERROR,
"机构标识与步骤一反馈给当前实训主体的机构标识不一致");
}
long identifierApplicationId = identifier.getApplicationId();
CurrencyGenerationRequest request = repository.findLatestBySubject(subject.getUserId(), subject.getSchoolId(),
subject.getClassId()).orElse(null);
CurrencyGenerationRequest input = invokeDomainWithResult(() -> CurrencyGenerationRequest.prepare(
identifierApplicationId, institutionIdentifier,
identifier.getFullInstitutionIdentifier(), amount, deliveryNodeCode, requestTimestamp));
if (request == null) {
long requestId = repository.save(input, subject.getUserId(), subject.getSchoolId(), subject.getClassId(),
operator);
request = CurrencyGenerationRequest.restore(requestId, input.getIdentifierApplicationId(),
input.getInstitutionIdentifier(), input.getFullInstitutionIdentifier(), input.getAmount(),
input.getDeliveryNodeCode(), input.getRequestTimestamp(), input.getRequestOriginalText(),
null, null, null, null, null, input.getStatus());
}
String digest = cryptography.sm3(input.getRequestOriginalText());
CurrencyGenerationRequest.Status expected = request.getStatus();
CurrencyGenerationRequest current = request;
invokeDomain(() -> current.replaceDigestInput(input.getIdentifierApplicationId(),
input.getInstitutionIdentifier(), input.getFullInstitutionIdentifier(), input.getAmount(),
input.getDeliveryNodeCode(), input.getRequestTimestamp(), digest));
repository.update(request, expected, "DIGEST", expected == CurrencyGenerationRequest.Status.DIGESTED
? "修改当前货币生成请求并重新计算SM3摘要"
: "初始化请求后使用SM3计算并保存请求摘要", operator);
return new CurrencyDigestResult(digest);
}
@Transactional
public CurrencyRequestResult sign(String privateKey, InstitutionKeySubject subject, String operator) {
CurrencyGenerationRequest request = requireCurrentIdentifierRequest(subject);
CurrencyGenerationRequest.Status expected = request.getStatus();
String signature = keyService.signCommercialBank(subject, privateKey, requireDigest(request));
if (signature == null) {
int errorSequence = errorRecorder.recordScoreError(subject.getUserId());
throw new BusinessException(ErrorCode.VALIDATION_ERROR,
"签名必须使用当前实训主体的商业银行第二私钥,本次实训第"
+ errorSequence + "次错误");
}
invokeDomain(() -> request.recordSignature(InstitutionKeyService.BANK_SECOND_KEY, signature, privateKey));
repository.update(request, expected, "SIGN", "使用商业银行第二私钥完成SM2签名", operator);
return new CurrencyRequestResult(request);
}
@Transactional
public CurrencyRequestResult packageRequest(InstitutionKeySubject subject, String operator) {
CurrencyGenerationRequest request = requireCurrentIdentifierRequest(subject);
return new CurrencyRequestResult(packageForSending(request, operator));
}
@Transactional
public CurrencyRequestResult send(InstitutionKeySubject subject, String operator) {
CurrencyGenerationRequest request = requireCurrentIdentifierRequest(subject);
CurrencyGenerationRequest.Status expected = request.getStatus();
invokeDomain(request::send);
repository.update(request, expected, "SEND", "商业银行发送货币生成请求至央行", operator);
expected = request.getStatus();
invokeDomain(request::receive);
repository.update(request, expected, "RECEIVE", "央行成功接收货币生成请求", operator);
scoreService.markStepCompleted(subject.getUserId(), 2);
return new CurrencyRequestResult(request);
}
private CurrencyGenerationRequest packageForSending(CurrencyGenerationRequest request, String operator) {
CurrencyGenerationRequest.Status expected = request.getStatus();
String message = buildRequestMessage(request);
invokeDomain(() -> request.packageMessage(message));
repository.update(request, expected, "PACKAGE", "发送前由后端自动组装货币生成请求报文", operator);
return request;
}
private static String buildRequestMessage(CurrencyGenerationRequest request) {
return "{\"requestId\":\"" + displayRequestId(request)
+ "\",\"orgId\":\"" + request.getInstitutionIdentifier()
+ "\",\"amount\":\"" + request.getAmount().setScale(2).toPlainString()
+ "\",\"systemId\":\"" + request.getDeliveryNodeCode()
+ "\",\"timestamp\":\"" + request.getRequestTimestamp()
+ "\",\"digest\":\"" + request.getRequestDigest()
+ "\",\"signature\":\"" + request.getBankSignature() + "\"}";
}
public static String displayRequestId(CurrencyGenerationRequest request) {
String date = request.getRequestTimestamp().substring(0, 8);
return "REQ_" + date + "_" + String.format("%03d", request.getId() % 1000);
}
@Transactional(readOnly = true)
public CurrencyRequestResult page(InstitutionKeySubject subject) {
return repository.findLatestBySubject(subject.getUserId(), subject.getSchoolId(), subject.getClassId())
.map(CurrencyRequestResult::new)
.orElseGet(CurrencyRequestResult::new);
}
@Transactional(readOnly = true)
public CurrencyGenerationRequest requireReceived(long requestId, InstitutionKeySubject subject) {
CurrencyGenerationRequest request = requireRequest(requestId, subject);
if (request.getStatus() != CurrencyGenerationRequest.Status.RECEIVED) {
throw new BusinessException(ErrorCode.VALIDATION_ERROR, "步骤二尚未完成央行接收,不能进入步骤三");
}
return request;
}
private CurrencyGenerationRequest requireRequest(long requestId, InstitutionKeySubject subject) {
return repository.findByIdAndSubject(requestId, subject.getUserId(), subject.getSchoolId(),
subject.getClassId()).orElseThrow(() ->
new BusinessException(ErrorCode.RESOURCE_NOT_FOUND,
"当前实训主体下的货币生成请求不存在或已删除"));
}
private CurrencyGenerationRequest requireCurrentIdentifierRequest(InstitutionKeySubject subject) {
IssuedInstitutionIdentifierResult identifier = identifierQueryService.requireLatestFeedbacked(subject);
CurrencyGenerationRequest request = requireLatestRequest(subject);
if (!identifier.getInstitutionIdentifier().equals(request.getInstitutionIdentifier())) {
throw new BusinessException(ErrorCode.RESOURCE_NOT_FOUND,
"当前步骤一机构标识尚未创建步骤二货币生成请求");
}
return request;
}
private CurrencyGenerationRequest requireLatestRequest(InstitutionKeySubject subject) {
return repository.findLatestBySubject(subject.getUserId(), subject.getSchoolId(), subject.getClassId())
.orElseThrow(() -> new BusinessException(ErrorCode.RESOURCE_NOT_FOUND,
"当前实训主体下没有可查询的货币生成请求"));
}
private static String requireOriginalText(CurrencyGenerationRequest request) {
if (request.getRequestOriginalText() == null) {
throw new BusinessException(ErrorCode.VALIDATION_ERROR, "请先完成请求原文拼接");
}
return request.getRequestOriginalText();
}
private static String requireDigest(CurrencyGenerationRequest request) {
if (request.getRequestDigest() == null) {
throw new BusinessException(ErrorCode.VALIDATION_ERROR, "请先完成SM3摘要计算");
}
return request.getRequestDigest();
}
private static void invokeDomain(Runnable operation) {
try {
operation.run();
} catch (IllegalArgumentException | IllegalStateException exception) {
throw new BusinessException(ErrorCode.VALIDATION_ERROR, exception.getMessage());
}
}
private static <T> T invokeDomainWithResult(DomainSupplier<T> supplier) {
try {
return supplier.get();
} catch (IllegalArgumentException | IllegalStateException exception) {
throw new BusinessException(ErrorCode.VALIDATION_ERROR, exception.getMessage());
}
}
private interface DomainSupplier<T> {
T get();
}
}