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.
201 lines
11 KiB
Java
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();
|
|
}
|
|
}
|