diff --git a/src/main/java/com/yau/digitalrmb/institutionidentity/application/ControlSystemSignatureService.java b/src/main/java/com/yau/digitalrmb/institutionidentity/application/ControlSystemSignatureService.java index 7f6fd6c..70e5db1 100644 --- a/src/main/java/com/yau/digitalrmb/institutionidentity/application/ControlSystemSignatureService.java +++ b/src/main/java/com/yau/digitalrmb/institutionidentity/application/ControlSystemSignatureService.java @@ -115,6 +115,11 @@ public class ControlSystemSignatureService { 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)); diff --git a/src/main/java/com/yau/digitalrmb/institutionidentity/application/CurrencyGenerationRequestService.java b/src/main/java/com/yau/digitalrmb/institutionidentity/application/CurrencyGenerationRequestService.java index d7398fa..002f0a5 100644 --- a/src/main/java/com/yau/digitalrmb/institutionidentity/application/CurrencyGenerationRequestService.java +++ b/src/main/java/com/yau/digitalrmb/institutionidentity/application/CurrencyGenerationRequestService.java @@ -43,12 +43,6 @@ public class CurrencyGenerationRequestService { long identifierApplicationId = identifier.getApplicationId(); CurrencyGenerationRequest request = repository.findLatestBySubject(subject.getUserId(), subject.getSchoolId(), subject.getClassId()).orElse(null); - if (request != null && request.getStatus() != CurrencyGenerationRequest.Status.CONCATENATED - && request.getStatus() != CurrencyGenerationRequest.Status.DIGESTED) { - throw new BusinessException(ErrorCode.VALIDATION_ERROR, - "当前最新货币生成请求已进入" + request.getStatus().name() - + ",不能重复创建;如需重新开始请先调用重新实现接口"); - } CurrencyGenerationRequest input = invokeDomainWithResult(() -> CurrencyGenerationRequest.prepare( identifierApplicationId, institutionIdentifier, identifier.getFullInstitutionIdentifier(), amount, deliveryNodeCode, requestTimestamp)); diff --git a/src/main/java/com/yau/digitalrmb/institutionidentity/application/InstitutionIdentifierStepService.java b/src/main/java/com/yau/digitalrmb/institutionidentity/application/InstitutionIdentifierStepService.java index 439b0be..26a6e37 100644 --- a/src/main/java/com/yau/digitalrmb/institutionidentity/application/InstitutionIdentifierStepService.java +++ b/src/main/java/com/yau/digitalrmb/institutionidentity/application/InstitutionIdentifierStepService.java @@ -64,13 +64,7 @@ public class InstitutionIdentifierStepService { subject.getSchoolId(), subject.getClassId()).orElse(null); if (application == null) { prepare(normalizedOriginalText, subject, operator); - application = requireCurrent(subject, InstitutionIdentifierApplication.Status.PREPARED, - "当前没有待计算摘要的本轮机构标识数据"); - } else if (application.getStatus() != InstitutionIdentifierApplication.Status.PREPARED - && application.getStatus() != InstitutionIdentifierApplication.Status.DIGESTED) { - throw new BusinessException(ErrorCode.VALIDATION_ERROR, - "当前最新一轮已进入" + application.getStatus().name() - + ",不能重复创建申请;如需重新开始请先调用重新实现接口"); + application = requireCurrent(subject, "当前没有待计算摘要的本轮机构标识数据"); } InstitutionIdentifierApplication.Status expectedStatus = application.getStatus(); String digest = cryptography.sm3(normalizedOriginalText); @@ -87,10 +81,11 @@ public class InstitutionIdentifierStepService { @Transactional public InstitutionSignatureResult sign(String privateKey, InstitutionKeySubject subject, String operator) { InstitutionIdentifierApplication application = requireCurrent(subject, - InstitutionIdentifierApplication.Status.DIGESTED, "当前没有待签名的本轮机构标识数据"); + "当前没有待签名的本轮机构标识数据"); + if (application.getDigest() == null) { + throw new BusinessException(ErrorCode.VALIDATION_ERROR, "请先完成SM3摘要计算"); + } long applicationId = application.getId(); - requireStatus(application, InstitutionIdentifierApplication.Status.DIGESTED, - "当前步骤不是商业银行签名,不能操作", operator); InstitutionIdentifierApplication.Status expectedStatus = application.getStatus(); String signature = keyService.signCommercialBank(subject, privateKey, application.getDigest()); if (signature == null) { @@ -106,9 +101,11 @@ public class InstitutionIdentifierStepService { @Transactional public InstitutionApplicationPackageResult packageApplication(InstitutionKeySubject subject, String operator) { InstitutionIdentifierApplication application = requireCurrent(subject, - InstitutionIdentifierApplication.Status.SIGNED, "当前没有待组装的本轮机构标识数据"); + "当前没有待组装的本轮机构标识数据"); + if (application.getBankSignature() == null) { + throw new BusinessException(ErrorCode.VALIDATION_ERROR, "请先完成商业银行签名"); + } long applicationId = application.getId(); - requireStatus(application, InstitutionIdentifierApplication.Status.SIGNED, "当前步骤不是申请组装,不能操作", operator); InstitutionIdentifierApplication.Status expectedStatus = application.getStatus(); invokeDomain(application::packageApplication); repository.update(application, expectedStatus, "PACKAGE", @@ -121,7 +118,10 @@ public class InstitutionIdentifierStepService { @Transactional public InstitutionApplicationPackageResult sendApplication(InstitutionKeySubject subject, String operator) { InstitutionIdentifierApplication application = requireCurrent(subject, - InstitutionIdentifierApplication.Status.PACKAGED, "当前没有待发送的已打包机构标识申请"); + "当前没有待发送的已打包机构标识申请"); + if (application.getBankSignature() == null) { + throw new BusinessException(ErrorCode.VALIDATION_ERROR, "请先完成申请报文组装"); + } InstitutionIdentifierApplication.Status expectedStatus = application.getStatus(); invokeDomain(application::submit); repository.update(application, expectedStatus, "SEND", @@ -131,17 +131,9 @@ public class InstitutionIdentifierStepService { application.getBankSignature()); } - private InstitutionIdentifierApplication requireCurrent(InstitutionKeySubject subject, - InstitutionIdentifierApplication.Status status, - String message) { - InstitutionIdentifierApplication application = repository.findLatestBySubject(subject.getUserId(), - subject.getSchoolId(), subject.getClassId()).orElseThrow(() -> - new BusinessException(ErrorCode.RESOURCE_NOT_FOUND, message)); - if (application.getStatus() != status) { - throw new BusinessException(ErrorCode.VALIDATION_ERROR, - message + ",当前最新一轮状态:" + application.getStatus().name()); - } - return application; + private InstitutionIdentifierApplication requireCurrent(InstitutionKeySubject subject, String message) { + return repository.findLatestBySubject(subject.getUserId(), subject.getSchoolId(), subject.getClassId()) + .orElseThrow(() -> new BusinessException(ErrorCode.RESOURCE_NOT_FOUND, message)); } private void requireStatus(InstitutionIdentifierApplication application, diff --git a/src/main/java/com/yau/digitalrmb/institutionidentity/application/QuotaControlBitService.java b/src/main/java/com/yau/digitalrmb/institutionidentity/application/QuotaControlBitService.java index 70f9391..41a24cc 100644 --- a/src/main/java/com/yau/digitalrmb/institutionidentity/application/QuotaControlBitService.java +++ b/src/main/java/com/yau/digitalrmb/institutionidentity/application/QuotaControlBitService.java @@ -149,10 +149,14 @@ public class QuotaControlBitService { public QuotaControlBitResult sign(String privateKey, String completeControlBit, String algorithm, InstitutionKeySubject subject, String operator) { QuotaControlBitEntity value = requireLatest(subject); - requireResult(value.getCompleteControlBit(), "请先拼接完整额度控制位"); + requireResult(value.getStaticSegment(), "请先生成静态段"); + requireResult(value.getDynamicSegment(), "请先生成动态段"); + requireResult(value.getCentralBankSignatureSegment(), "请先读取央行签名段"); + String expectedControlBit = value.getStaticSegment() + "|" + value.getDynamicSegment() + "|" + + value.getCentralBankSignatureSegment(); String submittedControlBit = completeControlBit == null ? null : completeControlBit.trim(); - if (!value.getCompleteControlBit().equals(submittedControlBit)) { - throw keyError(subject, "完整额度控制位与步骤八拼接结果不一致"); + if (!expectedControlBit.equals(submittedControlBit)) { + throw keyError(subject, "完整额度控制位与静态段、动态段及央行签名段拼接结果不一致"); } if (!"SM2".equalsIgnoreCase(algorithm == null ? "" : algorithm.trim())) { throw keyError(subject, "签名算法必须选择SM2"); @@ -163,6 +167,7 @@ public class QuotaControlBitService { } update(value, value.getStatus(), "SIGNED", operator, new LambdaUpdateWrapper() + .set(QuotaControlBitEntity::getCompleteControlBit, submittedControlBit) .set(QuotaControlBitEntity::getSigningKeyId, InstitutionKeyService.BANK_SECOND_KEY) .set(QuotaControlBitEntity::getHashAlgorithm, "SM2") .set(QuotaControlBitEntity::getBankSignature, signature) diff --git a/src/main/java/com/yau/digitalrmb/institutionidentity/application/StandardCurrencyListItemResult.java b/src/main/java/com/yau/digitalrmb/institutionidentity/application/StandardCurrencyListItemResult.java new file mode 100644 index 0000000..403c689 --- /dev/null +++ b/src/main/java/com/yau/digitalrmb/institutionidentity/application/StandardCurrencyListItemResult.java @@ -0,0 +1,23 @@ +package com.yau.digitalrmb.institutionidentity.application; + +import com.yau.digitalrmb.institutionidentity.infrastructure.StandardCurrencyEntity; +import lombok.Getter; + +import java.math.BigDecimal; + +@Getter +public class StandardCurrencyListItemResult { + private final Integer sequenceNumber; + private final BigDecimal denomination; + private final String currencyId; + private final String completeCurrency; + private final Integer status; + + public StandardCurrencyListItemResult(StandardCurrencyEntity value) { + this.sequenceNumber = value.getSequenceNumber(); + this.denomination = value.getDenomination(); + this.currencyId = value.getCurrencyId(); + this.completeCurrency = value.getCompleteCurrency(); + this.status = "可用".equals(value.getStatus()) || "已生效".equals(value.getStatus()) ? 2 : 1; + } +} diff --git a/src/main/java/com/yau/digitalrmb/institutionidentity/application/StandardCurrencyService.java b/src/main/java/com/yau/digitalrmb/institutionidentity/application/StandardCurrencyService.java index e9ea6a6..97ad52e 100644 --- a/src/main/java/com/yau/digitalrmb/institutionidentity/application/StandardCurrencyService.java +++ b/src/main/java/com/yau/digitalrmb/institutionidentity/application/StandardCurrencyService.java @@ -43,11 +43,8 @@ public class StandardCurrencyService { @Resource private InstitutionTrainingScoreService scoreService; @Transactional - public StandardCurrencyBatchResult decompose(BigDecimal amount, InstitutionKeySubject subject, String operator) { + public StandardCurrencyBatchResult decompose(InstitutionKeySubject subject, String operator) { QuotaControlBitEntity source = requireStepEight(subject); - if (amount == null || source.getAmount().compareTo(amount) != 0) { - throw inputError(subject, "用户输入的货币总额与步骤八额度控制位金额不一致"); - } StandardCurrencyBatchEntity current = latest(subject); List breakdown = decomposeAmount(source.getAmount()); if (current != null) { @@ -210,6 +207,16 @@ public class StandardCurrencyService { return result(requireLatest(subject)); } + @Transactional(readOnly = true) + public List currencyList(InstitutionKeySubject subject) { + StandardCurrencyBatchEntity batch = requireLatest(subject); + return currencyMapper.selectList(new LambdaQueryWrapper() + .eq(StandardCurrencyEntity::getBatchId, batch.getId()) + .eq(StandardCurrencyEntity::getDeleted, false) + .orderByAsc(StandardCurrencyEntity::getSequenceNumber)) + .stream().map(StandardCurrencyListItemResult::new).collect(Collectors.toList()); + } + private List decomposeAmount(BigDecimal amount) { long remaining = amount.movePointRight(2).longValueExact(); List result = new ArrayList<>(); diff --git a/src/main/java/com/yau/digitalrmb/institutionidentity/application/TransactionInformationIdentifierService.java b/src/main/java/com/yau/digitalrmb/institutionidentity/application/TransactionInformationIdentifierService.java index 8390a48..26ff412 100644 --- a/src/main/java/com/yau/digitalrmb/institutionidentity/application/TransactionInformationIdentifierService.java +++ b/src/main/java/com/yau/digitalrmb/institutionidentity/application/TransactionInformationIdentifierService.java @@ -132,8 +132,10 @@ public class TransactionInformationIdentifierService { @Transactional public TransactionIdentifierAssemblyResult assemble(InstitutionKeySubject subject, String operator) { - TransactionInformationIdentifier value = requireStatus(subject, - TransactionInformationIdentifier.Status.DIGESTED, "请先使用SM3生成摘要"); + TransactionInformationIdentifier value = requireLatest(subject); + if (value.getDigest() == null) { + throw new BusinessException(ErrorCode.VALIDATION_ERROR, "请先使用SM3生成摘要"); + } TransactionInformationIdentifier.Status expected = value.getStatus(); invoke(value::assemble); repository.update(value, expected, "ASSEMBLE", "将固定前缀TXN_与SM3摘要组装为交易信息标识", operator); @@ -196,8 +198,10 @@ public class TransactionInformationIdentifierService { @Transactional public QuotaRequestMessageResult packageQuotaRequest(InstitutionKeySubject subject, String operator) { - TransactionInformationIdentifier value = requireStatus(subject, - TransactionInformationIdentifier.Status.QUOTA_SIGNED, "请先完成步骤五额度请求签名"); + TransactionInformationIdentifier value = requireLatest(subject); + if (value.getQuotaSignature() == null) { + throw new BusinessException(ErrorCode.VALIDATION_ERROR, "请先完成步骤五额度请求签名"); + } TransactionInformationIdentifier.Status expected = value.getStatus(); String message = buildQuotaRequestMessage(value); invoke(() -> value.packageQuotaRequest(message)); @@ -207,8 +211,10 @@ public class TransactionInformationIdentifierService { @Transactional public QuotaRequestMessageResult sendQuotaRequest(InstitutionKeySubject subject, String operator) { - TransactionInformationIdentifier value = requireStatus(subject, - TransactionInformationIdentifier.Status.QUOTA_PACKAGED, "请先打包步骤五额度请求报文"); + TransactionInformationIdentifier value = requireLatest(subject); + if (value.getQuotaRequestMessage() == null) { + throw new BusinessException(ErrorCode.VALIDATION_ERROR, "请先打包步骤五额度请求报文"); + } TransactionInformationIdentifier.Status expected = value.getStatus(); invoke(value::sendQuotaRequest); invoke(value::receiveQuotaRequest); @@ -302,8 +308,10 @@ public class TransactionInformationIdentifierService { @Transactional(readOnly = true) public QuotaVerificationResult quotaVerificationDetail(InstitutionKeySubject subject) { - TransactionInformationIdentifier value = requireStatus(subject, - TransactionInformationIdentifier.Status.QUOTA_VERIFIED, "步骤六尚未完成额度请求验签"); + TransactionInformationIdentifier value = requireLatest(subject); + if (!Boolean.TRUE.equals(value.getQuotaDigestMatches())) { + throw new BusinessException(ErrorCode.VALIDATION_ERROR, "步骤六尚未完成额度请求验签"); + } return new QuotaVerificationResult(value); } diff --git a/src/main/java/com/yau/digitalrmb/institutionidentity/domain/CurrencyGenerationRequest.java b/src/main/java/com/yau/digitalrmb/institutionidentity/domain/CurrencyGenerationRequest.java index e9d82d7..03ea1b8 100644 --- a/src/main/java/com/yau/digitalrmb/institutionidentity/domain/CurrencyGenerationRequest.java +++ b/src/main/java/com/yau/digitalrmb/institutionidentity/domain/CurrencyGenerationRequest.java @@ -91,9 +91,6 @@ public class CurrencyGenerationRequest { public void replaceDigestInput(long identifierApplicationId, String institutionIdentifier, String fullInstitutionIdentifier, BigDecimal amount, String deliveryNodeCode, String requestTimestamp, String digest) { - if (status != Status.CONCATENATED && status != Status.DIGESTED) { - throw new IllegalStateException("当前货币生成请求已进入后续步骤,不能修改摘要输入"); - } CurrencyGenerationRequest replacement = prepare(identifierApplicationId, institutionIdentifier, fullInstitutionIdentifier, amount, deliveryNodeCode, requestTimestamp); if (digest == null || !digest.matches("[0-9A-F]{64}")) { @@ -107,6 +104,9 @@ public class CurrencyGenerationRequest { this.requestTimestamp = replacement.requestTimestamp; this.requestOriginalText = replacement.requestOriginalText; this.requestDigest = digest; + this.bankSignature = null; + this.signingKeyId = null; + this.requestMessage = null; this.status = Status.DIGESTED; } @@ -120,17 +120,22 @@ public class CurrencyGenerationRequest { } public void recordSignature(String keyId, String signature) { - requireStatus(Status.DIGESTED, "当前步骤不是商业银行签名,不能操作"); + if (requestDigest == null) { + throw new IllegalStateException("请先完成SM3摘要计算"); + } if (keyId == null || signature == null || !signature.matches("[0-9A-F]+")) { throw new IllegalArgumentException("SM2签名信息不正确"); } signingKeyId = keyId; bankSignature = signature; + requestMessage = null; status = Status.SIGNED; } public void packageMessage(String message) { - requireStatus(Status.SIGNED, "当前步骤不是请求报文组装,不能操作"); + if (bankSignature == null) { + throw new IllegalStateException("请先完成商业银行签名"); + } if (message == null || message.trim().isEmpty()) { throw new IllegalArgumentException("请求报文不能为空"); } @@ -139,12 +144,16 @@ public class CurrencyGenerationRequest { } public void send() { - requireStatus(Status.PACKAGED, "当前步骤不是请求发送,不能操作"); + if (requestMessage == null) { + throw new IllegalStateException("请先完成请求报文组装"); + } status = Status.SENT; } public void receive() { - requireStatus(Status.SENT, "央行只能接收已发送的请求"); + if (requestMessage == null) { + throw new IllegalStateException("央行只能接收已发送的请求"); + } status = Status.RECEIVED; } diff --git a/src/main/java/com/yau/digitalrmb/institutionidentity/domain/CurrencyGenerationVerification.java b/src/main/java/com/yau/digitalrmb/institutionidentity/domain/CurrencyGenerationVerification.java index b203cae..820e29a 100644 --- a/src/main/java/com/yau/digitalrmb/institutionidentity/domain/CurrencyGenerationVerification.java +++ b/src/main/java/com/yau/digitalrmb/institutionidentity/domain/CurrencyGenerationVerification.java @@ -133,7 +133,9 @@ public class CurrencyGenerationVerification { } public void verifyQuota(BigDecimal total, BigDecimal used, BigDecimal remaining) { - requireStatus(Status.DIGEST_MATCHED, "请先匹配摘要一和摘要二"); + if (!Boolean.TRUE.equals(digestMatches)) { + throw new IllegalStateException("请先匹配摘要一和摘要二"); + } totalQuota = total; usedQuota = used; remainingQuota = remaining; @@ -144,7 +146,9 @@ public class CurrencyGenerationVerification { } public void confirm(String keyId, String signature, String time) { - requireStatus(Status.QUOTA_VERIFIED, "请先完成额度检查"); + if (!Boolean.TRUE.equals(quotaSufficient)) { + throw new IllegalStateException("请先完成额度检查"); + } if (keyId == null || signature == null || time == null) { throw new IllegalArgumentException("央行签名确权数据不完整"); } @@ -155,7 +159,9 @@ public class CurrencyGenerationVerification { } public void packageResponse(String message) { - requireStatus(Status.CONFIRMED, "请先完成央行签名确权"); + if (centralBankSignature == null || confirmationTime == null) { + throw new IllegalStateException("请先完成央行签名确权"); + } if (message == null || message.trim().isEmpty()) { throw new IllegalArgumentException("确认报文不能为空"); } @@ -164,7 +170,9 @@ public class CurrencyGenerationVerification { } public void returnResponse() { - requireStatus(Status.PACKAGED, "请先打包确认报文"); + if (confirmationMessage == null) { + throw new IllegalStateException("请先打包确认报文"); + } status = Status.RESPONSE_RETURNED; } diff --git a/src/main/java/com/yau/digitalrmb/institutionidentity/domain/InstitutionIdentifierApplication.java b/src/main/java/com/yau/digitalrmb/institutionidentity/domain/InstitutionIdentifierApplication.java index 81eb858..02f3b48 100644 --- a/src/main/java/com/yau/digitalrmb/institutionidentity/domain/InstitutionIdentifierApplication.java +++ b/src/main/java/com/yau/digitalrmb/institutionidentity/domain/InstitutionIdentifierApplication.java @@ -67,9 +67,6 @@ public class InstitutionIdentifierApplication { } public void replaceDigestInput(String bankCode, String timestamp, String originalText, String digest) { - if (status != Status.PREPARED && status != Status.DIGESTED) { - throw new IllegalStateException("当前申请已进入后续步骤,不能修改摘要原文"); - } validateBase(bankCode, timestamp); this.bankCode = bankCode; this.timestamp = timestamp; @@ -78,6 +75,13 @@ public class InstitutionIdentifierApplication { throw new IllegalArgumentException("SM3摘要格式不正确"); } this.digest = digest; + this.bankSignature = null; + this.verificationKeyId = null; + this.signatureValid = null; + this.digestMatches = null; + this.signingKeyId = null; + this.institutionIdentifier = null; + this.centralBankSignature = null; this.status = Status.DIGESTED; } @@ -91,26 +95,40 @@ public class InstitutionIdentifierApplication { } public void recordBankSignature(String bankSignature) { - requireStatus(Status.DIGESTED, "当前步骤不是商业银行签名,不能操作"); + if (digest == null) { + throw new IllegalStateException("请先完成SM3摘要计算"); + } if (bankSignature == null || !bankSignature.matches("[0-9A-F]+")) { throw new IllegalArgumentException("银行SM2签名格式不正确"); } this.bankSignature = bankSignature; + this.verificationKeyId = null; + this.signatureValid = null; + this.digestMatches = null; + this.signingKeyId = null; + this.institutionIdentifier = null; + this.centralBankSignature = null; this.status = Status.SIGNED; } public void packageApplication() { - requireStatus(Status.SIGNED, "当前步骤不是申请组装,不能操作"); + if (bankSignature == null) { + throw new IllegalStateException("请先完成商业银行签名"); + } this.status = Status.PACKAGED; } public void submit() { - requireStatus(Status.PACKAGED, "当前步骤不是发送申请,不能操作"); + if (bankSignature == null) { + throw new IllegalStateException("请先完成申请报文组装"); + } this.status = Status.SUBMITTED; } public void recordVerification(String verificationKeyId, boolean signatureValid, boolean digestMatches) { - requireStatus(Status.SUBMITTED, "申请尚未发送至央行,不能进行解密验证"); + if (bankSignature == null) { + throw new IllegalStateException("申请尚未发送至央行,不能进行解密验证"); + } this.verificationKeyId = verificationKeyId; this.signatureValid = signatureValid; this.digestMatches = digestMatches; @@ -118,7 +136,9 @@ public class InstitutionIdentifierApplication { } public void issue(String signingKeyId, String institutionIdentifier, String centralBankSignature) { - requireStatus(Status.VERIFIED, "申请尚未验证通过,不能进行央行签名确权"); + if (!Boolean.TRUE.equals(signatureValid) || !Boolean.TRUE.equals(digestMatches)) { + throw new IllegalStateException("申请尚未验证通过,不能进行央行签名确权"); + } if (signingKeyId == null || institutionIdentifier == null || centralBankSignature == null) { throw new IllegalArgumentException("签发信息不能为空"); } diff --git a/src/main/java/com/yau/digitalrmb/institutionidentity/interfaces/dto/QuotaControlSignRequest.java b/src/main/java/com/yau/digitalrmb/institutionidentity/interfaces/dto/QuotaControlSignRequest.java index 70fc8cd..776ce1d 100644 --- a/src/main/java/com/yau/digitalrmb/institutionidentity/interfaces/dto/QuotaControlSignRequest.java +++ b/src/main/java/com/yau/digitalrmb/institutionidentity/interfaces/dto/QuotaControlSignRequest.java @@ -9,8 +9,8 @@ public class QuotaControlSignRequest { @NotBlank(message = "商业银行第二私钥不能为空") private String privateKey; @NotBlank(message = "完整额度控制位不能为空") - @Schema(description = "前端拼接得到的完整额度控制位,格式为静态段|动态段|央行控制系统签名段", - example = "STATIC_7AD7D47A81F0230562F912AC5CFA6F44D5A851C978191F4DAB8241CF1BFD7971|DYN_YMUL9SQ0_20260807141518|CB_CTRL_30450220ABCDEF") +// @Schema(description = "前端拼接得到的完整额度控制位,格式为静态段|动态段|央行控制系统签名段", +// example = "STATIC_7AD7D47A81F0230562F912AC5CFA6F44D5A851C978191F4DAB8241CF1BFD7971|DYN_YMUL9SQ0_20260807141518|CB_CTRL_30450220ABCDEF") private String completeControlBit; @NotBlank(message = "签名算法不能为空") @Schema(description = "必须为SM2", example = "SM2") diff --git a/src/main/java/com/yau/digitalrmb/institutionidentity/interfaces/rest/QuotaControlBitController.java b/src/main/java/com/yau/digitalrmb/institutionidentity/interfaces/rest/QuotaControlBitController.java index 1b89bf8..5c4136d 100644 --- a/src/main/java/com/yau/digitalrmb/institutionidentity/interfaces/rest/QuotaControlBitController.java +++ b/src/main/java/com/yau/digitalrmb/institutionidentity/interfaces/rest/QuotaControlBitController.java @@ -75,7 +75,7 @@ public class QuotaControlBitController { @PostMapping("/steps/sign") @Operation(summary = "商业银行第二私钥签名确认", - description = "前端提交完整额度控制位、商业银行第二SM2私钥和SM2算法。后端校验完整额度控制位与步骤八拼接结果一致后进行签名;错误内容、私钥或算法会累计实训错误次数。") + description = "前端提交由静态段、动态段和央行签名段拼接的完整额度控制位,以及商业银行第二SM2私钥和SM2算法;无需提前调用后端拼接接口。后端校验三段拼接结果一致后落库并签名,错误内容、私钥或算法会累计实训错误次数。") public ApiResponse sign(@Valid @RequestBody QuotaControlSignRequest request) { JwtUser user = AuthContextHolder.get(); QuotaControlBitResult result = service.sign(request.getPrivateKey(), request.getCompleteControlBit(), diff --git a/src/main/java/com/yau/digitalrmb/institutionidentity/interfaces/rest/StandardCurrencyController.java b/src/main/java/com/yau/digitalrmb/institutionidentity/interfaces/rest/StandardCurrencyController.java index c20d067..d930468 100644 --- a/src/main/java/com/yau/digitalrmb/institutionidentity/interfaces/rest/StandardCurrencyController.java +++ b/src/main/java/com/yau/digitalrmb/institutionidentity/interfaces/rest/StandardCurrencyController.java @@ -2,8 +2,8 @@ package com.yau.digitalrmb.institutionidentity.interfaces.rest; import com.yau.digitalrmb.institutionidentity.application.InstitutionKeySubject; import com.yau.digitalrmb.institutionidentity.application.StandardCurrencyBatchResult; +import com.yau.digitalrmb.institutionidentity.application.StandardCurrencyListItemResult; import com.yau.digitalrmb.institutionidentity.application.StandardCurrencyService; -import com.yau.digitalrmb.institutionidentity.interfaces.dto.CurrencyDecompositionRequest; import com.yau.digitalrmb.institutionidentity.interfaces.dto.CurrencyRuleRequest; import com.yau.digitalrmb.shared.api.ApiResponse; import com.yau.digitalrmb.shared.web.TraceIdFilter; @@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import javax.validation.Valid; +import java.util.List; @Validated @RestController @@ -30,12 +31,12 @@ public class StandardCurrencyController { @Resource private StandardCurrencyService service; @PostMapping("/steps/decompose") - @Operation(summary = "执行面额拆解", description = "按100、50、20、10、5、1、0.5、0.2、0.1、0.05、0.01从大到小拆解。") - public ApiResponse decompose( - @Valid @RequestBody CurrencyDecompositionRequest request) { + @Operation(summary = "执行面额拆解", + description = "无需请求体。后端按当前JWT主体查询步骤八已确认额度控制位金额,并按100、50、20、10、5、1、0.5、0.2、0.1、0.05、0.01从大到小拆解。") + public ApiResponse decompose() { JwtUser user = AuthContextHolder.get(); - return ApiResponse.success(service.decompose(request.getAmount(), InstitutionKeySubject.from(user), - user.getUsername()), traceId()); + return ApiResponse.success(service.decompose( + InstitutionKeySubject.from(user), user.getUsername()), traceId()); } @PostMapping("/steps/confirm-rule") @@ -53,6 +54,12 @@ public class StandardCurrencyController { return ApiResponse.success(service.generate(InstitutionKeySubject.from(user), user.getUsername()), traceId()); } + @GetMapping("/currencies") + @Operation(summary = "查询标准面额币串列表", description = "返回当前用户生成的全部币串;status=1表示待生效,status=2表示已生效。") + public ApiResponse> currencies() { + return ApiResponse.success(service.currencyList(InstitutionKeySubject.from(AuthContextHolder.get())), traceId()); + } + @GetMapping @Operation(summary = "查询步骤九当前结果") public ApiResponse detail() { diff --git a/src/test/java/com/yau/digitalrmb/institutionidentity/TransactionInformationIdentifierControllerTest.java b/src/test/java/com/yau/digitalrmb/institutionidentity/TransactionInformationIdentifierControllerTest.java index 3b4112c..72d02ad 100644 --- a/src/test/java/com/yau/digitalrmb/institutionidentity/TransactionInformationIdentifierControllerTest.java +++ b/src/test/java/com/yau/digitalrmb/institutionidentity/TransactionInformationIdentifierControllerTest.java @@ -584,12 +584,10 @@ class TransactionInformationIdentifierControllerTest { .andExpect(jsonPath("$.data.length()").value(1)) .andExpect(jsonPath("$.data.status").doesNotExist()) .andExpect(jsonPath("$.data.completeControlBit").doesNotExist()); - mvc.perform(post(QUOTA_CONTROL_BASE_PATH + "/steps/assemble").header("Authorization", authorization)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.data.completeControlBit").value(org.hamcrest.Matchers.allOf( - org.hamcrest.Matchers.containsString("|DYN_"), - org.hamcrest.Matchers.containsString("|CB_CTRL_")))); - String completeControlBit = quotaControlBitMapper.selectList(null).get(0).getCompleteControlBit(); + QuotaControlBitEntity beforeSign = quotaControlBitMapper.selectList(null).get(0); + assertThat(beforeSign.getCompleteControlBit()).isNull(); + String completeControlBit = beforeSign.getStaticSegment() + "|" + beforeSign.getDynamicSegment() + "|" + + beforeSign.getCentralBankSignatureSegment(); mvc.perform(post(QUOTA_CONTROL_BASE_PATH + "/steps/sign") .header("Authorization", authorization).contentType("application/json") @@ -603,7 +601,7 @@ class TransactionInformationIdentifierControllerTest { + completeControlBit + "_TAMPERED\",\"hashAlgorithm\":\"SM2\"}")) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.message").value(org.hamcrest.Matchers.allOf( - org.hamcrest.Matchers.containsString("完整额度控制位与步骤八拼接结果不一致"), + org.hamcrest.Matchers.containsString("完整额度控制位与静态段、动态段及央行签名段拼接结果不一致"), org.hamcrest.Matchers.containsString("第5次错误")))); mvc.perform(post(QUOTA_CONTROL_BASE_PATH + "/steps/sign") .header("Authorization", authorization).contentType("application/json") @@ -618,6 +616,8 @@ class TransactionInformationIdentifierControllerTest { .andExpect(status().isOk()) .andExpect(jsonPath("$.data.signature").value(org.hamcrest.Matchers.matchesPattern("[0-9A-F]+"))) .andExpect(jsonPath("$.data.length()").value(1)); + assertThat(quotaControlBitMapper.selectList(null).get(0).getCompleteControlBit()) + .isEqualTo(completeControlBit); mvc.perform(post(QUOTA_CONTROL_BASE_PATH + "/steps/package").header("Authorization", authorization)) .andExpect(status().isOk()).andExpect(jsonPath("$.data.status").value("PACKAGED")) .andExpect(jsonPath("$.data.requestMessage").value(org.hamcrest.Matchers.containsString("\"quotaId\""))); @@ -684,13 +684,7 @@ class TransactionInformationIdentifierControllerTest { flowsUserInputThroughCompleteStepEightAndAccumulatesErrors(); mvc.perform(post(STANDARD_CURRENCY_BASE_PATH + "/steps/decompose") - .header("Authorization", authorization).contentType("application/json") - .content("{\"amount\":1590.85}")) - .andExpect(status().isBadRequest()) - .andExpect(jsonPath("$.message").value(org.hamcrest.Matchers.containsString("第7次错误"))); - mvc.perform(post(STANDARD_CURRENCY_BASE_PATH + "/steps/decompose") - .header("Authorization", authorization).contentType("application/json") - .content("{\"amount\":1589.85}")) + .header("Authorization", authorization)) .andExpect(status().isOk()) .andExpect(jsonPath("$.data.status").value("DECOMPOSED")) .andExpect(jsonPath("$.data.totalCount").value(27)) @@ -703,7 +697,7 @@ class TransactionInformationIdentifierControllerTest { .content("{\"prefix\":\"RMB\",\"idRule\":\"批次号+枚序号+随机校验码\"," + "\"currencyStatus\":\"待生效\"}")) .andExpect(status().isBadRequest()) - .andExpect(jsonPath("$.message").value(org.hamcrest.Matchers.containsString("第8次错误"))); + .andExpect(jsonPath("$.message").value(org.hamcrest.Matchers.containsString("第7次错误"))); mvc.perform(post(STANDARD_CURRENCY_BASE_PATH + "/steps/confirm-rule") .header("Authorization", authorization).contentType("application/json") .content("{\"prefix\":\"DC\",\"idRule\":\"批次号+枚序号+随机校验码\"," + @@ -722,6 +716,19 @@ class TransactionInformationIdentifierControllerTest { org.hamcrest.Matchers.containsString("_ORG_3A4B5C6D7E8F_100.00_"))) .andExpect(jsonPath("$.data.currencies[26].denomination").value(0.05)) .andExpect(jsonPath("$.data.currencies[26].status").value("待生效")); + mvc.perform(get(STANDARD_CURRENCY_BASE_PATH + "/currencies") + .header("Authorization", authorization)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.length()").value(27)) + .andExpect(jsonPath("$.data[0].sequenceNumber").value(1)) + .andExpect(jsonPath("$.data[0].denomination").value(100.0)) + .andExpect(jsonPath("$.data[0].currencyId").value( + org.hamcrest.Matchers.matchesPattern("DC_[0-9]{8}_[0-9]{3}_001_[0-9A-Z]{6}"))) + .andExpect(jsonPath("$.data[0].completeCurrency").value( + org.hamcrest.Matchers.containsString("_ORG_3A4B5C6D7E8F_100.00_"))) + .andExpect(jsonPath("$.data[0].status").value(1)) + .andExpect(jsonPath("$.data[0].randomCheckCode").doesNotExist()) + .andExpect(jsonPath("$.data[0].institutionIdentifier").doesNotExist()); List currencies = standardCurrencyMapper.selectList(null); assertThat(standardCurrencyBatchMapper.selectList(null)).hasSize(1); @@ -729,6 +736,17 @@ class TransactionInformationIdentifierControllerTest { assertThat(currencies.stream().map(StandardCurrencyEntity::getDenomination) .reduce(BigDecimal.ZERO, BigDecimal::add)).isEqualByComparingTo("1589.85"); assertThat(currencies.stream().map(StandardCurrencyEntity::getCurrencyId).distinct().count()).isEqualTo(27); + StandardCurrencyEntity activated = currencies.get(0); + activated.setStatus("可用"); + standardCurrencyMapper.updateById(activated); + mvc.perform(get(STANDARD_CURRENCY_BASE_PATH + "/currencies") + .header("Authorization", authorization)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.length()").value(27)) + .andExpect(jsonPath("$.data[0].status").value(2)) + .andExpect(jsonPath("$.data[1].status").value(1)); + activated.setStatus("待生效"); + standardCurrencyMapper.updateById(activated); List currencyIds = currencies.stream().map(StandardCurrencyEntity::getId) .collect(java.util.stream.Collectors.toList()); long batchId = standardCurrencyBatchMapper.selectList(null).get(0).getId(); @@ -746,8 +764,7 @@ class TransactionInformationIdentifierControllerTest { @Test void rejectsStepNineUntilStepEightIsReceived() throws Exception { mvc.perform(post(STANDARD_CURRENCY_BASE_PATH + "/steps/decompose") - .header("Authorization", authorization).contentType("application/json") - .content("{\"amount\":1589.85}")) + .header("Authorization", authorization)) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.message").value(org.hamcrest.Matchers.containsString( "步骤八尚未完成央行接收")));