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.
950 lines
63 KiB
Java
950 lines
63 KiB
Java
package com.yau.digitalrmb.institutionidentity;
|
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
import com.yau.digitalrmb.institutionidentity.application.InstitutionKeyService;
|
|
import com.yau.digitalrmb.institutionidentity.application.InstitutionKeySubject;
|
|
import com.yau.digitalrmb.institutionidentity.domain.CurrencyGenerationRequest;
|
|
import com.yau.digitalrmb.institutionidentity.domain.CurrencyGenerationVerification;
|
|
import com.yau.digitalrmb.institutionidentity.domain.InstitutionIdentityCryptography;
|
|
import com.yau.digitalrmb.institutionidentity.infrastructure.CurrencyGenerationRequestEntity;
|
|
import com.yau.digitalrmb.institutionidentity.infrastructure.CurrencyGenerationRequestMapper;
|
|
import com.yau.digitalrmb.institutionidentity.infrastructure.CurrencyGenerationVerificationEntity;
|
|
import com.yau.digitalrmb.institutionidentity.infrastructure.CurrencyGenerationVerificationMapper;
|
|
import com.yau.digitalrmb.institutionidentity.infrastructure.ControlSystemSignatureEntity;
|
|
import com.yau.digitalrmb.institutionidentity.infrastructure.ControlSystemSignatureMapper;
|
|
import com.yau.digitalrmb.institutionidentity.infrastructure.InstitutionIdentifierApplicationEntity;
|
|
import com.yau.digitalrmb.institutionidentity.infrastructure.QuotaControlBitEntity;
|
|
import com.yau.digitalrmb.institutionidentity.infrastructure.QuotaControlBitMapper;
|
|
import com.yau.digitalrmb.institutionidentity.infrastructure.InstitutionIdentifierApplicationMapper;
|
|
import com.yau.digitalrmb.institutionidentity.infrastructure.StandardCurrencyBatchMapper;
|
|
import com.yau.digitalrmb.institutionidentity.infrastructure.StandardCurrencyEntity;
|
|
import com.yau.digitalrmb.institutionidentity.infrastructure.StandardCurrencyMapper;
|
|
import com.yau.digitalrmb.institutionidentity.infrastructure.StuModuleScoreDetailsEntity;
|
|
import com.yau.digitalrmb.institutionidentity.infrastructure.StuModuleScoreDetailsMapper;
|
|
import com.yau.digitalrmb.institutionidentity.infrastructure.TransactionInformationIdentifierEntity;
|
|
import com.yau.digitalrmb.institutionidentity.infrastructure.TransactionInformationIdentifierMapper;
|
|
import com.yau.digitalrmb.institutionidentity.infrastructure.TransactionInformationIdentifierOperationLogEntity;
|
|
import com.yau.digitalrmb.institutionidentity.infrastructure.TransactionInformationIdentifierOperationLogMapper;
|
|
import com.yau.digitalrmb.security.application.JwtTokenService;
|
|
import org.junit.jupiter.api.BeforeEach;
|
|
import org.junit.jupiter.api.Test;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
|
import org.springframework.boot.test.context.SpringBootTest;
|
|
import org.springframework.jdbc.core.JdbcTemplate;
|
|
import org.springframework.test.context.ActiveProfiles;
|
|
import org.springframework.test.web.servlet.MockMvc;
|
|
|
|
import java.math.BigDecimal;
|
|
import java.time.LocalDateTime;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
|
|
|
@SpringBootTest
|
|
@AutoConfigureMockMvc
|
|
@ActiveProfiles("test")
|
|
class TransactionInformationIdentifierControllerTest {
|
|
private static final String BASE_PATH =
|
|
"/api/v1/institution-identifiers/transaction-information-identifiers";
|
|
private static final String QUOTA_BASE_PATH =
|
|
"/api/v1/institution-identifiers/quota-requests";
|
|
private static final String CENTRAL_QUOTA_REQUEST_BASE_PATH =
|
|
"/api/v1/institution-identifiers/central-bank/quota-requests";
|
|
private static final String VERIFICATION_BASE_PATH =
|
|
"/api/v1/institution-identifiers/quota-verifications";
|
|
private static final String CONTROL_SIGNATURE_BASE_PATH =
|
|
"/api/v1/institution-identifiers/control-system-signatures";
|
|
private static final String COMMERCIAL_BANK_CONTROL_SIGNATURE_BASE_PATH =
|
|
"/api/v1/institution-identifiers/commercial-bank/control-system-signatures";
|
|
private static final String QUOTA_CONTROL_BASE_PATH =
|
|
"/api/v1/institution-identifiers/quota-control-bits";
|
|
private static final String CENTRAL_QUOTA_CONTROL_BASE_PATH =
|
|
"/api/v1/institution-identifiers/central-bank/quota-control-bits";
|
|
private static final String STANDARD_CURRENCY_BASE_PATH =
|
|
"/api/v1/institution-identifiers/standard-currencies";
|
|
private static final String USER_ID = "00000000-0000-0000-0000-000000009004";
|
|
private static final long SCHOOL_ID = 1001L;
|
|
private static final long CLASS_ID = 2001L;
|
|
|
|
@Autowired private MockMvc mvc;
|
|
@Autowired private JwtTokenService jwtTokenService;
|
|
@Autowired private JdbcTemplate jdbcTemplate;
|
|
@Autowired private InstitutionIdentifierApplicationMapper applicationMapper;
|
|
@Autowired private CurrencyGenerationRequestMapper requestMapper;
|
|
@Autowired private CurrencyGenerationVerificationMapper verificationMapper;
|
|
@Autowired private TransactionInformationIdentifierMapper transactionMapper;
|
|
@Autowired private TransactionInformationIdentifierOperationLogMapper transactionLogMapper;
|
|
@Autowired private ControlSystemSignatureMapper controlSignatureMapper;
|
|
@Autowired private QuotaControlBitMapper quotaControlBitMapper;
|
|
@Autowired private StandardCurrencyBatchMapper standardCurrencyBatchMapper;
|
|
@Autowired private StandardCurrencyMapper standardCurrencyMapper;
|
|
@Autowired private StuModuleScoreDetailsMapper scoreMapper;
|
|
@Autowired private InstitutionKeyService keyService;
|
|
@Autowired private InstitutionIdentityCryptography cryptography;
|
|
|
|
private String authorization;
|
|
|
|
@BeforeEach
|
|
void setUp() {
|
|
jdbcTemplate.update("DELETE FROM standard_currency");
|
|
jdbcTemplate.update("DELETE FROM standard_currency_batch");
|
|
jdbcTemplate.update("DELETE FROM quota_control_bit");
|
|
jdbcTemplate.update("DELETE FROM control_system_signature");
|
|
jdbcTemplate.update("DELETE FROM transaction_information_identifier_operation_log");
|
|
jdbcTemplate.update("DELETE FROM transaction_information_identifier");
|
|
jdbcTemplate.update("DELETE FROM currency_generation_verification_operation_log");
|
|
jdbcTemplate.update("DELETE FROM currency_generation_verification");
|
|
jdbcTemplate.update("DELETE FROM currency_generation_request_operation_log");
|
|
jdbcTemplate.update("DELETE FROM currency_generation_request");
|
|
jdbcTemplate.update("DELETE FROM institution_identifier_training_error");
|
|
jdbcTemplate.update("DELETE FROM institution_identifier_operation_log");
|
|
jdbcTemplate.update("DELETE FROM institution_identifier_application");
|
|
jdbcTemplate.update("DELETE FROM institution_sm2_key_audit");
|
|
jdbcTemplate.update("DELETE FROM institution_sm2_key");
|
|
jdbcTemplate.update("DELETE FROM stu_module_score_details");
|
|
authorization = "Bearer " + jwtTokenService.issueFor(USER_ID, SCHOOL_ID, CLASS_ID,
|
|
"module-four-tester", Collections.singleton("STUDENT")).accessToken();
|
|
}
|
|
|
|
@Test
|
|
void rejectsDigestWhenStepThreeHasNotReturnedResponseAndPrepareRouteIsRemoved() throws Exception {
|
|
mvc.perform(post(BASE_PATH + "/steps/prepare").header("Authorization", authorization))
|
|
.andExpect(status().isNotFound());
|
|
mvc.perform(post(BASE_PATH + "/steps/digest").header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"originalText\":\"1589.85|ORG_3A4B5C6D7E8F|20260730153330\"}"))
|
|
.andExpect(status().isBadRequest())
|
|
.andExpect(jsonPath("$.message").value(org.hamcrest.Matchers.containsString(
|
|
"步骤三尚未完成确认报文返回")));
|
|
assertThat(transactionMapper.selectList(null)).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
void recordsEveryConcatenationErrorAndReturnsFixedMessage() throws Exception {
|
|
insertCompletedPrecedingData();
|
|
mvc.perform(post(BASE_PATH + "/steps/digest").header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"originalText\":\"1589.85[ORG_3A4B5C6D7E8F|20260730153330\"}"))
|
|
.andExpect(status().isBadRequest())
|
|
.andExpect(jsonPath("$.message").value("数据拼接错误"));
|
|
mvc.perform(post(BASE_PATH + "/steps/digest").header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"originalText\":\"999.00|ORG_3A4B5C6D7E8F|20260730153330\"}"))
|
|
.andExpect(status().isBadRequest())
|
|
.andExpect(jsonPath("$.message").value("数据拼接错误"));
|
|
|
|
assertThat(transactionMapper.selectList(null)).isEmpty();
|
|
StuModuleScoreDetailsEntity score = scoreMapper.selectOne(
|
|
new LambdaQueryWrapper<StuModuleScoreDetailsEntity>()
|
|
.eq(StuModuleScoreDetailsEntity::getUserId, USER_ID)
|
|
.eq(StuModuleScoreDetailsEntity::getSerialNumber, 3));
|
|
assertThat(score).isNotNull();
|
|
assertThat(score.getCompletionStatus()).isEqualTo("2");
|
|
}
|
|
|
|
@Test
|
|
void flowsCompletedPreviousDataAndPersistsEveryModuleFourStep() throws Exception {
|
|
PrecedingData preceding = insertCompletedPrecedingData();
|
|
|
|
String originalText = "1589.85|ORG_3A4B5C6D7E8F|20260730153330";
|
|
mvc.perform(post(BASE_PATH + "/steps/digest").header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"originalText\":\"" + originalText + "\"}"))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.digest")
|
|
.value(org.hamcrest.Matchers.matchesPattern("[0-9A-F]{64}")))
|
|
.andExpect(jsonPath("$.data.status").doesNotExist())
|
|
.andExpect(jsonPath("$.data.originalText").doesNotExist())
|
|
.andExpect(jsonPath("$.data.transactionTimestamp").doesNotExist())
|
|
.andExpect(jsonPath("$.data.digestAlgorithm").doesNotExist());
|
|
|
|
mvc.perform(post(BASE_PATH + "/steps/assemble").header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.transactionIdentifier")
|
|
.value(org.hamcrest.Matchers.matchesPattern("TXN_[0-9A-F]{64}")))
|
|
.andExpect(jsonPath("$.data.status").doesNotExist())
|
|
.andExpect(jsonPath("$.data.digest").doesNotExist());
|
|
|
|
mvc.perform(get(BASE_PATH).header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.status").value("ASSEMBLED"))
|
|
.andExpect(jsonPath("$.data.transactionIdentifier")
|
|
.value(org.hamcrest.Matchers.matchesPattern("TXN_[0-9A-F]{64}")));
|
|
|
|
List<TransactionInformationIdentifierEntity> records = transactionMapper.selectList(null);
|
|
assertThat(records).hasSize(1);
|
|
TransactionInformationIdentifierEntity persisted = records.get(0);
|
|
assertThat(persisted.getVerificationId()).isEqualTo(preceding.verificationId);
|
|
assertThat(persisted.getRequestId()).isEqualTo(preceding.requestId);
|
|
assertThat(persisted.getIdentifierApplicationId()).isEqualTo(preceding.applicationId);
|
|
assertThat(persisted.getAmount()).isEqualByComparingTo("1589.85");
|
|
assertThat(persisted.getInstitutionIdentifier()).isEqualTo("ORG_3A4B5C6D7E8F");
|
|
assertThat(persisted.getOriginalText()).isEqualTo(originalText);
|
|
assertThat(persisted.getTransactionTimestamp()).isEqualTo("20260730153330");
|
|
assertThat(persisted.getDigest()).isEqualTo(cryptography.sm3(originalText));
|
|
assertThat(persisted.getTransactionIdentifier()).isEqualTo("TXN_" + persisted.getDigest());
|
|
assertThat(persisted.getStatus()).isEqualTo("ASSEMBLED");
|
|
|
|
assertThat(transactionLogMapper.selectList(null))
|
|
.extracting(TransactionInformationIdentifierOperationLogEntity::getOperation)
|
|
.containsExactly("DIGEST", "ASSEMBLE");
|
|
|
|
StuModuleScoreDetailsEntity score = scoreMapper.selectOne(
|
|
new LambdaQueryWrapper<StuModuleScoreDetailsEntity>()
|
|
.eq(StuModuleScoreDetailsEntity::getUserId, USER_ID)
|
|
.eq(StuModuleScoreDetailsEntity::getSerialNumber, 3));
|
|
assertThat(score).isNotNull();
|
|
assertThat(score.getSchedule()).isEqualTo(44.44);
|
|
assertThat(score.getScoreProject()).isEqualTo(44.44);
|
|
}
|
|
|
|
@Test
|
|
void flowsCompleteStepFiveFromSourceDataThroughCentralBankReceive() throws Exception {
|
|
insertCompletedPrecedingData();
|
|
mvc.perform(post(BASE_PATH + "/steps/digest").header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"originalText\":\"1589.85|ORG_3A4B5C6D7E8F|20260730153330\"}"))
|
|
.andExpect(status().isOk());
|
|
mvc.perform(post(BASE_PATH + "/steps/assemble").header("Authorization", authorization))
|
|
.andExpect(status().isOk());
|
|
|
|
mvc.perform(get(QUOTA_BASE_PATH + "/source-data").header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.amount").value(1589.85))
|
|
.andExpect(jsonPath("$.data.institutionIdentifier").value("ORG_3A4B5C6D7E8F"))
|
|
.andExpect(jsonPath("$.data.transactionIdentifier")
|
|
.value(org.hamcrest.Matchers.matchesPattern("TXN_[0-9A-F]{64}")))
|
|
.andExpect(jsonPath("$.data.length()").value(3))
|
|
.andExpect(jsonPath("$.data.transactionIdentifierExists").doesNotExist())
|
|
.andExpect(jsonPath("$.data.conclusion").doesNotExist());
|
|
|
|
mvc.perform(post(QUOTA_BASE_PATH + "/steps/prepare").header("Authorization", authorization))
|
|
.andExpect(status().isNotFound());
|
|
String quotaOriginalText = "1589.85|ORG_3A4B5C6D7E8F|"
|
|
+ transactionMapper.selectList(null).get(0).getTransactionIdentifier();
|
|
mvc.perform(post(QUOTA_BASE_PATH + "/steps/digest").header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"originalText\":\"" + quotaOriginalText + "\"}"))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.digest").value(
|
|
org.hamcrest.Matchers.matchesPattern("[0-9A-F]{64}")))
|
|
.andExpect(jsonPath("$.data.length()").value(1))
|
|
.andExpect(jsonPath("$.data.status").doesNotExist())
|
|
.andExpect(jsonPath("$.data.quotaRequestId").doesNotExist())
|
|
.andExpect(jsonPath("$.data.quotaOriginalText").doesNotExist());
|
|
|
|
InstitutionKeySubject subject = new InstitutionKeySubject(USER_ID, SCHOOL_ID, CLASS_ID);
|
|
String bankPrivateKey = keyService.commercialBankKey(subject, "module-four-tester").getPrivateKey();
|
|
mvc.perform(post(QUOTA_BASE_PATH + "/steps/sign").header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"privateKey\":\"" + bankPrivateKey + "\"}"))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.signature").value(
|
|
org.hamcrest.Matchers.matchesPattern("[0-9A-F]+")))
|
|
.andExpect(jsonPath("$.data.length()").value(1))
|
|
.andExpect(jsonPath("$.data.status").doesNotExist())
|
|
.andExpect(jsonPath("$.data.quotaSigningKeyId").doesNotExist())
|
|
.andExpect(jsonPath("$.data.quotaSignature").doesNotExist());
|
|
long stepFiveRecordId = transactionMapper.selectList(null).get(0).getId();
|
|
mvc.perform(post(QUOTA_BASE_PATH + "/steps/digest").header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"originalText\":\"" + quotaOriginalText + "\"}"))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.digest").value(org.hamcrest.Matchers.matchesPattern("[0-9A-F]{64}")));
|
|
assertThat(transactionMapper.selectList(null)).hasSize(1);
|
|
assertThat(transactionMapper.selectList(null).get(0).getId()).isEqualTo(stepFiveRecordId);
|
|
assertThat(transactionMapper.selectList(null).get(0).getQuotaSignature()).isNull();
|
|
mvc.perform(post(QUOTA_BASE_PATH + "/steps/sign").header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"privateKey\":\"" + bankPrivateKey + "\"}"))
|
|
.andExpect(status().isOk());
|
|
mvc.perform(post(QUOTA_BASE_PATH + "/steps/sign").header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"privateKey\":\"" + bankPrivateKey + "\"}"))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.signature").value(org.hamcrest.Matchers.matchesPattern("[0-9A-F]+")));
|
|
assertThat(transactionMapper.selectList(null)).hasSize(1);
|
|
assertThat(transactionMapper.selectList(null).get(0).getId()).isEqualTo(stepFiveRecordId);
|
|
mvc.perform(post(QUOTA_BASE_PATH + "/steps/package").header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.length()").value(6))
|
|
.andExpect(jsonPath("$.data.quotaRequestId").isNotEmpty())
|
|
.andExpect(jsonPath("$.data.amount").value(1589.85))
|
|
.andExpect(jsonPath("$.data.orgId").value("ORG_3A4B5C6D7E8F"))
|
|
.andExpect(jsonPath("$.data.txnId").value(org.hamcrest.Matchers.startsWith("TXN_")))
|
|
.andExpect(jsonPath("$.data.digest").value(org.hamcrest.Matchers.matchesPattern("[0-9A-F]{64}")))
|
|
.andExpect(jsonPath("$.data.signature").isNotEmpty())
|
|
.andExpect(jsonPath("$.data.status").doesNotExist())
|
|
.andExpect(jsonPath("$.data.quotaRequestMessage").doesNotExist());
|
|
mvc.perform(get(CENTRAL_QUOTA_REQUEST_BASE_PATH).header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.status").value(1))
|
|
.andExpect(jsonPath("$.data.length()").value(7));
|
|
mvc.perform(post(QUOTA_BASE_PATH + "/steps/send").header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.length()").value(6))
|
|
.andExpect(jsonPath("$.data.quotaRequestId").isNotEmpty())
|
|
.andExpect(jsonPath("$.data.amount").value(1589.85))
|
|
.andExpect(jsonPath("$.data.orgId").value("ORG_3A4B5C6D7E8F"))
|
|
.andExpect(jsonPath("$.data.txnId").value(org.hamcrest.Matchers.startsWith("TXN_")))
|
|
.andExpect(jsonPath("$.data.digest").value(org.hamcrest.Matchers.matchesPattern("[0-9A-F]{64}")))
|
|
.andExpect(jsonPath("$.data.signature").isNotEmpty())
|
|
.andExpect(jsonPath("$.data.status").doesNotExist())
|
|
.andExpect(jsonPath("$.data.centralBankReceiveStatus").doesNotExist());
|
|
mvc.perform(get(QUOTA_BASE_PATH).header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.length()").value(6));
|
|
mvc.perform(get(CENTRAL_QUOTA_REQUEST_BASE_PATH).header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.status").value(2))
|
|
.andExpect(jsonPath("$.data.quotaRequestId").isNotEmpty())
|
|
.andExpect(jsonPath("$.data.amount").value(1589.85))
|
|
.andExpect(jsonPath("$.data.orgId").value("ORG_3A4B5C6D7E8F"))
|
|
.andExpect(jsonPath("$.data.txnId").value(org.hamcrest.Matchers.startsWith("TXN_")))
|
|
.andExpect(jsonPath("$.data.digest").value(org.hamcrest.Matchers.matchesPattern("[0-9A-F]{64}")))
|
|
.andExpect(jsonPath("$.data.signature").isNotEmpty())
|
|
.andExpect(jsonPath("$.data.length()").value(7))
|
|
.andExpect(jsonPath("$.data.requestMessage").doesNotExist());
|
|
mvc.perform(post(CENTRAL_QUOTA_REQUEST_BASE_PATH + "/steps/receive")
|
|
.header("Authorization", authorization))
|
|
.andExpect(status().isNotFound());
|
|
|
|
TransactionInformationIdentifierEntity persisted = transactionMapper.selectList(null).get(0);
|
|
assertThat(persisted.getStatus()).isEqualTo("QUOTA_RECEIVED");
|
|
assertThat(persisted.getQuotaDigest()).isEqualTo(cryptography.sm3(persisted.getQuotaOriginalText()));
|
|
assertThat(cryptography.verify(keyService.commercialBankKey(subject, "module-four-tester").getPublicKey(),
|
|
persisted.getQuotaDigest(), persisted.getQuotaSignature())).isTrue();
|
|
assertThat(persisted.getQuotaRequestMessage()).contains(persisted.getQuotaRequestId(),
|
|
persisted.getQuotaDigest(), persisted.getQuotaSignature());
|
|
assertThat(transactionLogMapper.selectList(null))
|
|
.extracting(TransactionInformationIdentifierOperationLogEntity::getOperation)
|
|
.containsExactly("DIGEST", "ASSEMBLE", "QUOTA_DIGEST", "QUOTA_SIGN",
|
|
"QUOTA_DIGEST", "QUOTA_SIGN", "QUOTA_SIGN",
|
|
"QUOTA_PACKAGE", "QUOTA_SEND");
|
|
}
|
|
|
|
@Test
|
|
void flowsStepSixDataThroughCentralBankSignatureAndCommercialBankReceive() throws Exception {
|
|
flowsCompleteStepFiveFromSourceDataThroughCentralBankReceive();
|
|
InstitutionKeySubject subject = new InstitutionKeySubject(USER_ID, SCHOOL_ID, CLASS_ID);
|
|
String bankPublicKey = keyService.commercialBankKey(subject, "module-four-tester").getPublicKey();
|
|
String centralPrivateKey = keyService.centralBankKey(subject, "module-four-tester").getPrivateKey();
|
|
|
|
String verificationOriginalText = transactionMapper.selectList(null).get(0).getQuotaOriginalText();
|
|
mvc.perform(post(VERIFICATION_BASE_PATH + "/steps/concatenate-original-text")
|
|
.header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.originalText").value(verificationOriginalText))
|
|
.andExpect(jsonPath("$.data.length()").value(1));
|
|
mvc.perform(post(VERIFICATION_BASE_PATH + "/steps/hash")
|
|
.header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"originalText\":\"" + verificationOriginalText + "\"}"))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.h1").value(org.hamcrest.Matchers.matchesPattern("[0-9A-F]{64}")))
|
|
.andExpect(jsonPath("$.data.h2").doesNotExist());
|
|
mvc.perform(post(VERIFICATION_BASE_PATH + "/steps/recover-signature-digest")
|
|
.header("Authorization", authorization).contentType("application/json")
|
|
.content("{\"publicKey\":\"WRONG_BANK_SECOND_PUBLIC_KEY\"}"))
|
|
.andExpect(status().isBadRequest())
|
|
.andExpect(jsonPath("$.message").value(org.hamcrest.Matchers.containsString("第1次错误")));
|
|
mvc.perform(post(VERIFICATION_BASE_PATH + "/steps/recover-signature-digest")
|
|
.header("Authorization", authorization).contentType("application/json")
|
|
.content("{\"publicKey\":\"" + bankPublicKey + "\"}"))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.h2").value(org.hamcrest.Matchers.matchesPattern("[0-9A-F]{64}")))
|
|
.andExpect(jsonPath("$.data.h1").doesNotExist());
|
|
long stepSixRecordId = transactionMapper.selectList(null).get(0).getId();
|
|
mvc.perform(post(VERIFICATION_BASE_PATH + "/steps/hash")
|
|
.header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"originalText\":\"" + verificationOriginalText + "\"}"))
|
|
.andExpect(status().isOk());
|
|
assertThat(transactionMapper.selectList(null)).hasSize(1);
|
|
assertThat(transactionMapper.selectList(null).get(0).getId()).isEqualTo(stepSixRecordId);
|
|
assertThat(transactionMapper.selectList(null).get(0).getQuotaSignatureDigest()).isNull();
|
|
mvc.perform(post(VERIFICATION_BASE_PATH + "/steps/recover-signature-digest")
|
|
.header("Authorization", authorization).contentType("application/json")
|
|
.content("{\"publicKey\":\"" + bankPublicKey + "\"}"))
|
|
.andExpect(status().isOk());
|
|
mvc.perform(post(VERIFICATION_BASE_PATH + "/steps/recover-signature-digest")
|
|
.header("Authorization", authorization).contentType("application/json")
|
|
.content("{\"publicKey\":\"" + bankPublicKey + "\"}"))
|
|
.andExpect(status().isOk());
|
|
assertThat(transactionMapper.selectList(null)).hasSize(1);
|
|
assertThat(transactionMapper.selectList(null).get(0).getId()).isEqualTo(stepSixRecordId);
|
|
mvc.perform(post(VERIFICATION_BASE_PATH + "/steps/compare-digests")
|
|
.header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.h1EqualsH2").value(true))
|
|
.andExpect(jsonPath("$.data.conclusion").value("一致,验签通过"));
|
|
|
|
TransactionInformationIdentifierEntity verified = transactionMapper.selectList(null).get(0);
|
|
assertThat(verified.getStatus()).isEqualTo("QUOTA_VERIFIED");
|
|
assertThat(verified.getQuotaRecomputedDigest()).isEqualTo(
|
|
cryptography.sm3(verified.getQuotaOriginalText()));
|
|
assertThat(verified.getQuotaSignatureDigest()).isEqualTo(verified.getQuotaDigest());
|
|
assertThat(verified.getQuotaSignatureValid()).isTrue();
|
|
assertThat(verified.getQuotaDigestMatches()).isTrue();
|
|
|
|
mvc.perform(post(VERIFICATION_BASE_PATH + "/steps/concatenate-original-text")
|
|
.header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.originalText").value(verificationOriginalText));
|
|
mvc.perform(post(VERIFICATION_BASE_PATH + "/steps/hash")
|
|
.header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"originalText\":\"" + verificationOriginalText + "\"}"))
|
|
.andExpect(status().isOk());
|
|
assertThat(transactionMapper.selectList(null).get(0).getQuotaSignatureDigest()).isNull();
|
|
mvc.perform(post(VERIFICATION_BASE_PATH + "/steps/recover-signature-digest")
|
|
.header("Authorization", authorization).contentType("application/json")
|
|
.content("{\"publicKey\":\"" + bankPublicKey + "\"}"))
|
|
.andExpect(status().isOk());
|
|
mvc.perform(post(VERIFICATION_BASE_PATH + "/steps/compare-digests")
|
|
.header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.h1EqualsH2").value(true));
|
|
mvc.perform(post(VERIFICATION_BASE_PATH + "/steps/compare-digests")
|
|
.header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.h1EqualsH2").value(true));
|
|
assertThat(transactionMapper.selectList(null)).hasSize(1);
|
|
assertThat(transactionMapper.selectList(null).get(0).getId()).isEqualTo(stepSixRecordId);
|
|
verified = transactionMapper.selectList(null).get(0);
|
|
|
|
mvc.perform(post(CONTROL_SIGNATURE_BASE_PATH + "/steps/prepare")
|
|
.header("Authorization", authorization))
|
|
.andExpect(status().isNotFound());
|
|
String controlOriginalText = verified.getAmount().setScale(2).toPlainString() + "|"
|
|
+ verified.getInstitutionIdentifier() + "|" + verified.getTransactionIdentifier();
|
|
mvc.perform(post(CONTROL_SIGNATURE_BASE_PATH + "/steps/digest")
|
|
.header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"originalText\":\"999.00|"
|
|
+ verified.getInstitutionIdentifier() + "|" + verified.getTransactionIdentifier()
|
|
+ "\"}"))
|
|
.andExpect(status().isBadRequest())
|
|
.andExpect(jsonPath("$.message").value(org.hamcrest.Matchers.allOf(
|
|
org.hamcrest.Matchers.containsString("与当前用户前序数据不一致"),
|
|
org.hamcrest.Matchers.containsString("第2次错误"))));
|
|
assertThat(controlSignatureMapper.selectList(null)).isEmpty();
|
|
String firstDigest = mvc.perform(post(CONTROL_SIGNATURE_BASE_PATH + "/steps/digest")
|
|
.header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"originalText\":\"" + controlOriginalText + "\"}"))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.digest").value(cryptography.sm3(controlOriginalText)))
|
|
.andExpect(jsonPath("$.data.length()").value(1))
|
|
.andExpect(jsonPath("$.data.status").doesNotExist())
|
|
.andExpect(jsonPath("$.data.originalText").doesNotExist())
|
|
.andExpect(jsonPath("$.data.controlId").doesNotExist())
|
|
.andReturn().getResponse().getContentAsString();
|
|
String repeatedDigest = mvc.perform(post(CONTROL_SIGNATURE_BASE_PATH + "/steps/digest")
|
|
.header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"originalText\":\"" + controlOriginalText + "\"}"))
|
|
.andExpect(status().isOk()).andReturn().getResponse().getContentAsString();
|
|
assertThat(com.jayway.jsonpath.JsonPath.<String>read(repeatedDigest, "$.data.digest"))
|
|
.isEqualTo(com.jayway.jsonpath.JsonPath.<String>read(firstDigest, "$.data.digest"));
|
|
assertThat(controlSignatureMapper.selectList(null)).hasSize(1);
|
|
mvc.perform(post(CONTROL_SIGNATURE_BASE_PATH + "/steps/sign")
|
|
.header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"privateKey\":\"WRONG_CENTRAL_PRIVATE_KEY\"}"))
|
|
.andExpect(status().isBadRequest())
|
|
.andExpect(jsonPath("$.message").value(org.hamcrest.Matchers.containsString("第3次错误")));
|
|
mvc.perform(post(CONTROL_SIGNATURE_BASE_PATH + "/steps/sign")
|
|
.header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"privateKey\":\"" + centralPrivateKey + "\"}"))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.signature").value(org.hamcrest.Matchers.matchesPattern("[0-9A-F]+")))
|
|
.andExpect(jsonPath("$.data.length()").value(1));
|
|
mvc.perform(post(CONTROL_SIGNATURE_BASE_PATH + "/steps/package").header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.status").value("PACKAGED"))
|
|
.andExpect(jsonPath("$.data.responseMessage").value(org.hamcrest.Matchers.containsString(
|
|
"\"cbCtrlSignature\"")));
|
|
mvc.perform(get(COMMERCIAL_BANK_CONTROL_SIGNATURE_BASE_PATH).header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.status").value(1))
|
|
.andExpect(jsonPath("$.data.ctrlId").isNotEmpty())
|
|
.andExpect(jsonPath("$.data.amount").value(1589.85))
|
|
.andExpect(jsonPath("$.data.orgId").value("ORG_3A4B5C6D7E8F"))
|
|
.andExpect(jsonPath("$.data.txnId").value(org.hamcrest.Matchers.startsWith("TXN_")))
|
|
.andExpect(jsonPath("$.data.cbCtrlSignature").isNotEmpty())
|
|
.andExpect(jsonPath("$.data.length()").value(6));
|
|
mvc.perform(post(CONTROL_SIGNATURE_BASE_PATH + "/steps/send").header("Authorization", authorization))
|
|
.andExpect(status().isOk()).andExpect(jsonPath("$.data.status").value("SENT"));
|
|
mvc.perform(get(COMMERCIAL_BANK_CONTROL_SIGNATURE_BASE_PATH).header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.status").value(2))
|
|
.andExpect(jsonPath("$.data.ctrlId").isNotEmpty())
|
|
.andExpect(jsonPath("$.data.amount").value(1589.85))
|
|
.andExpect(jsonPath("$.data.orgId").value("ORG_3A4B5C6D7E8F"))
|
|
.andExpect(jsonPath("$.data.txnId").value(org.hamcrest.Matchers.startsWith("TXN_")))
|
|
.andExpect(jsonPath("$.data.cbCtrlSignature").isNotEmpty())
|
|
.andExpect(jsonPath("$.data.length()").value(6));
|
|
|
|
ControlSystemSignatureEntity persisted = controlSignatureMapper.selectList(null).get(0);
|
|
TransactionInformationIdentifierEntity transaction = transactionMapper.selectList(null).get(0);
|
|
assertThat(persisted.getTransactionIdentifierId()).isEqualTo(transaction.getId());
|
|
assertThat(persisted.getOriginalText()).isEqualTo(
|
|
transaction.getAmount().setScale(2).toPlainString() + "|"
|
|
+ transaction.getInstitutionIdentifier() + "|" + transaction.getTransactionIdentifier());
|
|
assertThat(persisted.getDigest()).isEqualTo(cryptography.sm3(persisted.getOriginalText()));
|
|
assertThat(persisted.getControlSignature()).isEqualTo(persisted.getSignature());
|
|
assertThat(cryptography.verify(keyService.centralBankKey(
|
|
new InstitutionKeySubject(USER_ID, SCHOOL_ID, CLASS_ID), "module-four-tester").getPublicKey(),
|
|
persisted.getDigest(), persisted.getControlSignature())).isTrue();
|
|
assertThat(persisted.getResponseMessage()).contains(persisted.getControlId(), persisted.getControlSignature());
|
|
assertThat(persisted.getStatus()).isEqualTo("SENT");
|
|
StuModuleScoreDetailsEntity score = scoreMapper.selectOne(
|
|
new LambdaQueryWrapper<StuModuleScoreDetailsEntity>()
|
|
.eq(StuModuleScoreDetailsEntity::getUserId, USER_ID)
|
|
.eq(StuModuleScoreDetailsEntity::getSerialNumber, 3));
|
|
assertThat(score.getCompletionStatus()).isEqualTo("3");
|
|
}
|
|
|
|
@Test
|
|
void flowsUserInputThroughCompleteStepEightAndAccumulatesErrors() throws Exception {
|
|
flowsStepSixDataThroughCentralBankSignatureAndCommercialBankReceive();
|
|
InstitutionKeySubject subject = new InstitutionKeySubject(USER_ID, SCHOOL_ID, CLASS_ID);
|
|
String bankPrivateKey = keyService.commercialBankKey(subject, "module-four-tester").getPrivateKey();
|
|
mvc.perform(get(CENTRAL_QUOTA_CONTROL_BASE_PATH).header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.status").value(1))
|
|
.andExpect(jsonPath("$.data.quotaId").value(org.hamcrest.Matchers.nullValue()))
|
|
.andExpect(jsonPath("$.data.length()").value(6));
|
|
mvc.perform(get(QUOTA_CONTROL_BASE_PATH + "/segments/static")
|
|
.header("Authorization", authorization))
|
|
.andExpect(status().isNotFound())
|
|
.andExpect(jsonPath("$.message").value("当前没有步骤八额度控制位记录"));
|
|
mvc.perform(get(QUOTA_CONTROL_BASE_PATH + "/segments/dynamic")
|
|
.header("Authorization", authorization))
|
|
.andExpect(status().isNotFound())
|
|
.andExpect(jsonPath("$.message").value("当前没有步骤八额度控制位记录"));
|
|
String staticSegmentResponse = mvc.perform(post(QUOTA_CONTROL_BASE_PATH + "/steps/static-segment")
|
|
.header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.staticSegment").value(
|
|
org.hamcrest.Matchers.matchesPattern("STATIC_[0-9A-F]{64}")))
|
|
.andExpect(jsonPath("$.data.length()").value(1))
|
|
.andExpect(jsonPath("$.data.status").doesNotExist())
|
|
.andReturn().getResponse().getContentAsString();
|
|
String staticSegment = com.jayway.jsonpath.JsonPath.read(staticSegmentResponse, "$.data.staticSegment");
|
|
mvc.perform(get(QUOTA_CONTROL_BASE_PATH + "/segments/static")
|
|
.header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.staticSegment").value(staticSegment))
|
|
.andExpect(jsonPath("$.data.length()").value(1));
|
|
mvc.perform(get(QUOTA_CONTROL_BASE_PATH + "/segments/dynamic")
|
|
.header("Authorization", authorization))
|
|
.andExpect(status().isNotFound())
|
|
.andExpect(jsonPath("$.message").value("当前尚未生成步骤八动态段"));
|
|
String firstDynamicSegmentResponse = mvc.perform(post(QUOTA_CONTROL_BASE_PATH + "/steps/dynamic-segment")
|
|
.header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.dynamicSegment").value(
|
|
org.hamcrest.Matchers.matchesPattern("DYN_[A-Z0-9]{8}_\\d{14}")))
|
|
.andExpect(jsonPath("$.data.length()").value(1))
|
|
.andExpect(jsonPath("$.data.status").doesNotExist())
|
|
.andReturn().getResponse().getContentAsString();
|
|
String firstDynamicSegment = com.jayway.jsonpath.JsonPath.read(
|
|
firstDynamicSegmentResponse, "$.data.dynamicSegment");
|
|
mvc.perform(get(QUOTA_CONTROL_BASE_PATH + "/segments/dynamic")
|
|
.header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.dynamicSegment").value(firstDynamicSegment))
|
|
.andExpect(jsonPath("$.data.length()").value(1));
|
|
long stepEightRecordId = quotaControlBitMapper.selectList(null).get(0).getId();
|
|
String secondDynamicSegmentResponse = mvc.perform(post(QUOTA_CONTROL_BASE_PATH + "/steps/dynamic-segment")
|
|
.header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.dynamicSegment").value(
|
|
org.hamcrest.Matchers.matchesPattern("DYN_[A-Z0-9]{8}_\\d{14}")))
|
|
.andReturn().getResponse().getContentAsString();
|
|
String secondDynamicSegment = com.jayway.jsonpath.JsonPath.read(
|
|
secondDynamicSegmentResponse, "$.data.dynamicSegment");
|
|
assertThat(secondDynamicSegment).isNotEqualTo(firstDynamicSegment);
|
|
assertThat(quotaControlBitMapper.selectList(null)).hasSize(1);
|
|
assertThat(quotaControlBitMapper.selectList(null).get(0).getId()).isEqualTo(stepEightRecordId);
|
|
mvc.perform(post(QUOTA_CONTROL_BASE_PATH + "/steps/read-central-bank-signature")
|
|
.header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.centralBankSignatureSegment").value(
|
|
org.hamcrest.Matchers.startsWith("CB_CTRL_")))
|
|
.andExpect(jsonPath("$.data.length()").value(1))
|
|
.andExpect(jsonPath("$.data.status").doesNotExist())
|
|
.andExpect(jsonPath("$.data.completeControlBit").doesNotExist());
|
|
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")
|
|
.content("{\"privateKey\":\"" + bankPrivateKey + "\",\"completeControlBit\":\""
|
|
+ completeControlBit + "\",\"hashAlgorithm\":\"SM3\"}"))
|
|
.andExpect(status().isBadRequest())
|
|
.andExpect(jsonPath("$.message").value(org.hamcrest.Matchers.containsString("第4次错误")));
|
|
mvc.perform(post(QUOTA_CONTROL_BASE_PATH + "/steps/sign")
|
|
.header("Authorization", authorization).contentType("application/json")
|
|
.content("{\"privateKey\":\"" + bankPrivateKey + "\",\"completeControlBit\":\""
|
|
+ completeControlBit + "_TAMPERED\",\"hashAlgorithm\":\"SM2\"}"))
|
|
.andExpect(status().isBadRequest())
|
|
.andExpect(jsonPath("$.message").value(org.hamcrest.Matchers.allOf(
|
|
org.hamcrest.Matchers.containsString("完整额度控制位与静态段、动态段及央行签名段拼接结果不一致"),
|
|
org.hamcrest.Matchers.containsString("第5次错误"))));
|
|
mvc.perform(post(QUOTA_CONTROL_BASE_PATH + "/steps/sign")
|
|
.header("Authorization", authorization).contentType("application/json")
|
|
.content("{\"privateKey\":\"WRONG_BANK_PRIVATE_KEY\",\"completeControlBit\":\""
|
|
+ completeControlBit + "\",\"hashAlgorithm\":\"SM2\"}"))
|
|
.andExpect(status().isBadRequest())
|
|
.andExpect(jsonPath("$.message").value(org.hamcrest.Matchers.containsString("第6次错误")));
|
|
mvc.perform(post(QUOTA_CONTROL_BASE_PATH + "/steps/sign")
|
|
.header("Authorization", authorization).contentType("application/json")
|
|
.content("{\"privateKey\":\"" + bankPrivateKey + "\",\"completeControlBit\":\""
|
|
+ completeControlBit + "\",\"hashAlgorithm\":\"SM2\"}"))
|
|
.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\"")));
|
|
mvc.perform(post(QUOTA_CONTROL_BASE_PATH + "/steps/static-segment")
|
|
.header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.staticSegment").value(
|
|
org.hamcrest.Matchers.matchesPattern("STATIC_[0-9A-F]{64}")));
|
|
QuotaControlBitEntity resetByStatic = quotaControlBitMapper.selectList(null).get(0);
|
|
assertThat(resetByStatic.getId()).isEqualTo(stepEightRecordId);
|
|
assertThat(resetByStatic.getStatus()).isEqualTo("STATIC_READY");
|
|
assertThat(resetByStatic.getDynamicSegment()).isNull();
|
|
assertThat(resetByStatic.getCentralBankSignatureSegment()).isNull();
|
|
assertThat(resetByStatic.getCompleteControlBit()).isNull();
|
|
assertThat(resetByStatic.getBankSignature()).isNull();
|
|
assertThat(resetByStatic.getRequestMessage()).isNull();
|
|
mvc.perform(post(QUOTA_CONTROL_BASE_PATH + "/steps/dynamic-segment")
|
|
.header("Authorization", authorization)).andExpect(status().isOk());
|
|
mvc.perform(post(QUOTA_CONTROL_BASE_PATH + "/steps/read-central-bank-signature")
|
|
.header("Authorization", authorization)).andExpect(status().isOk());
|
|
mvc.perform(post(QUOTA_CONTROL_BASE_PATH + "/steps/assemble")
|
|
.header("Authorization", authorization)).andExpect(status().isOk());
|
|
String regeneratedCompleteControlBit = quotaControlBitMapper.selectList(null).get(0).getCompleteControlBit();
|
|
mvc.perform(post(QUOTA_CONTROL_BASE_PATH + "/steps/sign")
|
|
.header("Authorization", authorization).contentType("application/json")
|
|
.content("{\"privateKey\":\"" + bankPrivateKey + "\",\"completeControlBit\":\""
|
|
+ regeneratedCompleteControlBit + "\",\"hashAlgorithm\":\"SM2\"}"))
|
|
.andExpect(status().isOk());
|
|
mvc.perform(post(QUOTA_CONTROL_BASE_PATH + "/steps/package").header("Authorization", authorization))
|
|
.andExpect(status().isOk());
|
|
mvc.perform(post(QUOTA_CONTROL_BASE_PATH + "/steps/send").header("Authorization", authorization))
|
|
.andExpect(status().isOk()).andExpect(jsonPath("$.data.status").value("RECEIVED"))
|
|
.andExpect(jsonPath("$.data.receiveStatus").value(2));
|
|
mvc.perform(get(CENTRAL_QUOTA_CONTROL_BASE_PATH).header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.status").value(2))
|
|
.andExpect(jsonPath("$.data.quotaId").value(org.hamcrest.Matchers.startsWith("QC_")))
|
|
.andExpect(jsonPath("$.data.staticSegment").value(
|
|
org.hamcrest.Matchers.matchesPattern("STATIC_[0-9A-F]{64}")))
|
|
.andExpect(jsonPath("$.data.dynamicSegment").value(
|
|
org.hamcrest.Matchers.matchesPattern("DYN_[A-Z0-9]{8}_\\d{14}")))
|
|
.andExpect(jsonPath("$.data.cbSignatureSegment").value(
|
|
org.hamcrest.Matchers.startsWith("CB_CTRL_")))
|
|
.andExpect(jsonPath("$.data.bankSignature").value(
|
|
org.hamcrest.Matchers.matchesPattern("[0-9A-F]+")))
|
|
.andExpect(jsonPath("$.data.length()").value(6));
|
|
mvc.perform(post(CENTRAL_QUOTA_CONTROL_BASE_PATH + "/steps/receive")
|
|
.header("Authorization", authorization))
|
|
.andExpect(status().isNotFound());
|
|
|
|
QuotaControlBitEntity stored = quotaControlBitMapper.selectList(null).get(0);
|
|
assertThat(stored.getStatus()).isEqualTo("RECEIVED");
|
|
assertThat(stored.getReceiveStatus()).isEqualTo(2);
|
|
assertThat(stored.getRequestMessage()).contains(stored.getQuotaId(), stored.getStaticSegment(),
|
|
stored.getDynamicSegment(), stored.getCentralBankSignatureSegment(), stored.getBankSignature());
|
|
StuModuleScoreDetailsEntity score = scoreMapper.selectOne(new LambdaQueryWrapper<StuModuleScoreDetailsEntity>()
|
|
.eq(StuModuleScoreDetailsEntity::getUserId, USER_ID)
|
|
.eq(StuModuleScoreDetailsEntity::getSerialNumber, 3));
|
|
assertThat(score.getCompletionStatus()).isEqualTo("6");
|
|
}
|
|
|
|
@Test
|
|
void generatesStepNineStandardCurrenciesAndAccumulatesInputErrors() throws Exception {
|
|
flowsUserInputThroughCompleteStepEightAndAccumulatesErrors();
|
|
|
|
mvc.perform(post(STANDARD_CURRENCY_BASE_PATH + "/steps/decompose")
|
|
.header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.status").value("DECOMPOSED"))
|
|
.andExpect(jsonPath("$.data.totalCount").value(27))
|
|
.andExpect(jsonPath("$.data.breakdown[0].denomination").value(100.0))
|
|
.andExpect(jsonPath("$.data.breakdown[0].count").value(15))
|
|
.andExpect(jsonPath("$.data.breakdown[10].denomination").value(0.01))
|
|
.andExpect(jsonPath("$.data.breakdown[10].count").value(0));
|
|
mvc.perform(post(STANDARD_CURRENCY_BASE_PATH + "/steps/confirm-rule")
|
|
.header("Authorization", authorization).contentType("application/json")
|
|
.content("{\"prefix\":\"RMB\",\"idRule\":\"批次号+枚序号+随机校验码\"," +
|
|
"\"currencyStatus\":\"待生效\"}"))
|
|
.andExpect(status().isBadRequest())
|
|
.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\":\"批次号+枚序号+随机校验码\"," +
|
|
"\"currencyStatus\":\"待生效\"}"))
|
|
.andExpect(status().isOk()).andExpect(jsonPath("$.data.status").value("RULE_CONFIRMED"));
|
|
mvc.perform(post(STANDARD_CURRENCY_BASE_PATH + "/steps/generate")
|
|
.header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.status").value("GENERATED"))
|
|
.andExpect(jsonPath("$.data.currencies.length()").value(27))
|
|
.andExpect(jsonPath("$.data.currencies[0].sequenceNumber").value(1))
|
|
.andExpect(jsonPath("$.data.currencies[0].denomination").value(100.0))
|
|
.andExpect(jsonPath("$.data.currencies[0].currencyId").value(
|
|
org.hamcrest.Matchers.matchesPattern("DC_[0-9]{8}_[0-9]{3}_001_[0-9A-Z]{6}")))
|
|
.andExpect(jsonPath("$.data.currencies[0].completeCurrency").value(
|
|
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<StandardCurrencyEntity> currencies = standardCurrencyMapper.selectList(null);
|
|
assertThat(standardCurrencyBatchMapper.selectList(null)).hasSize(1);
|
|
assertThat(currencies).hasSize(27);
|
|
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<Long> currencyIds = currencies.stream().map(StandardCurrencyEntity::getId)
|
|
.collect(java.util.stream.Collectors.toList());
|
|
long batchId = standardCurrencyBatchMapper.selectList(null).get(0).getId();
|
|
mvc.perform(post(STANDARD_CURRENCY_BASE_PATH + "/steps/generate").header("Authorization", authorization))
|
|
.andExpect(status().isOk())
|
|
.andExpect(jsonPath("$.data.status").value("GENERATED"))
|
|
.andExpect(jsonPath("$.data.currencies.length()").value(27));
|
|
assertThat(standardCurrencyBatchMapper.selectList(null)).hasSize(1);
|
|
assertThat(standardCurrencyBatchMapper.selectList(null).get(0).getId()).isEqualTo(batchId);
|
|
assertThat(standardCurrencyMapper.selectList(null)).hasSize(27);
|
|
assertThat(standardCurrencyMapper.selectList(null).stream().map(StandardCurrencyEntity::getId)
|
|
.collect(java.util.stream.Collectors.toList())).containsExactlyElementsOf(currencyIds);
|
|
}
|
|
|
|
@Test
|
|
void rejectsStepNineUntilStepEightIsReceived() throws Exception {
|
|
mvc.perform(post(STANDARD_CURRENCY_BASE_PATH + "/steps/decompose")
|
|
.header("Authorization", authorization))
|
|
.andExpect(status().isBadRequest())
|
|
.andExpect(jsonPath("$.message").value(org.hamcrest.Matchers.containsString(
|
|
"步骤八尚未完成央行接收")));
|
|
assertThat(standardCurrencyBatchMapper.selectList(null)).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
void rejectsStepEightUntilStepSevenIsReceived() throws Exception {
|
|
mvc.perform(post(QUOTA_CONTROL_BASE_PATH + "/steps/static-segment")
|
|
.header("Authorization", authorization).contentType("application/json")
|
|
.content("{\"amount\":1589.85,\"institutionIdentifier\":\"ORG_3A4B5C6D7E8F\"," +
|
|
"\"transactionIdentifier\":\"TXN_ABC\"}"))
|
|
.andExpect(status().isBadRequest())
|
|
.andExpect(jsonPath("$.message").value(org.hamcrest.Matchers.containsString(
|
|
"步骤七尚未完成商业银行接收")));
|
|
assertThat(quotaControlBitMapper.selectList(null)).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
void rejectsIncorrectStepFiveOriginalTextAndRecordsError() throws Exception {
|
|
insertCompletedPrecedingData();
|
|
mvc.perform(post(BASE_PATH + "/steps/digest").header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"originalText\":\"1589.85|ORG_3A4B5C6D7E8F|20260730153330\"}"))
|
|
.andExpect(status().isOk());
|
|
mvc.perform(post(BASE_PATH + "/steps/assemble").header("Authorization", authorization))
|
|
.andExpect(status().isOk());
|
|
|
|
mvc.perform(post(QUOTA_BASE_PATH + "/steps/digest").header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"originalText\":\"999.00|ORG_3A4B5C6D7E8F|TXN_"
|
|
+ "7A8B9C0D1E2F3A4B5C6D7E8F9A0B1C2D3E4F5A6B7C8D9E0F1A2B3C4D5E6F\"}"))
|
|
.andExpect(status().isBadRequest())
|
|
.andExpect(jsonPath("$.message").value("数据拼接错误"));
|
|
assertThat(transactionMapper.selectList(null).get(0).getStatus()).isEqualTo("ASSEMBLED");
|
|
}
|
|
|
|
@Test
|
|
void rejectsQuotaVerificationUntilStepFiveIsReceived() throws Exception {
|
|
insertCompletedPrecedingData();
|
|
mvc.perform(post(VERIFICATION_BASE_PATH + "/steps/hash")
|
|
.header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"originalText\":\"1589.85|ORG_3A4B5C6D7E8F|TXN_"
|
|
+ "7A8B9C0D1E2F3A4B5C6D7E8F9A0B1C2D3E4F5A6B7C8D9E0F1A2B3C4D5E6F\"}"))
|
|
.andExpect(status().isNotFound())
|
|
.andExpect(jsonPath("$.message").value(org.hamcrest.Matchers.containsString(
|
|
"当前没有额度请求记录")));
|
|
}
|
|
|
|
@Test
|
|
void rejectsQuotaRequestUntilModuleFourIsAssembled() throws Exception {
|
|
insertCompletedPrecedingData();
|
|
mvc.perform(get(QUOTA_BASE_PATH + "/source-data").header("Authorization", authorization))
|
|
.andExpect(status().isBadRequest())
|
|
.andExpect(jsonPath("$.message").value(org.hamcrest.Matchers.containsString(
|
|
"步骤四尚未完成交易信息标识")));
|
|
mvc.perform(post(QUOTA_BASE_PATH + "/steps/digest").header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"originalText\":\"1589.85|ORG_3A4B5C6D7E8F|TXN_"
|
|
+ "7A8B9C0D1E2F3A4B5C6D7E8F9A0B1C2D3E4F5A6B7C8D9E0F1A2B3C4D5E6F\"}"))
|
|
.andExpect(status().isBadRequest());
|
|
assertThat(transactionMapper.selectList(null)).isEmpty();
|
|
}
|
|
|
|
@Test
|
|
void repeatedCorrectDigestUpdatesExistingRecordWithoutAddingData() throws Exception {
|
|
insertCompletedPrecedingData();
|
|
mvc.perform(post(BASE_PATH + "/steps/digest").header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"originalText\":\"1589.85|ORG_3A4B5C6D7E8F|20260730153330\"}"))
|
|
.andExpect(status().isOk());
|
|
long id = transactionMapper.selectList(null).get(0).getId();
|
|
mvc.perform(post(BASE_PATH + "/steps/assemble").header("Authorization", authorization))
|
|
.andExpect(status().isOk());
|
|
mvc.perform(post(BASE_PATH + "/steps/digest").header("Authorization", authorization)
|
|
.contentType("application/json")
|
|
.content("{\"originalText\":\"1589.85|ORG_3A4B5C6D7E8F|20260730154444\"}"))
|
|
.andExpect(status().isOk());
|
|
|
|
assertThat(transactionMapper.selectList(null)).hasSize(1);
|
|
TransactionInformationIdentifierEntity updated = transactionMapper.selectList(null).get(0);
|
|
assertThat(updated.getId()).isEqualTo(id);
|
|
assertThat(updated.getTransactionTimestamp()).isEqualTo("20260730154444");
|
|
assertThat(updated.getStatus()).isEqualTo("DIGESTED");
|
|
assertThat(updated.getTransactionIdentifier()).isNull();
|
|
}
|
|
|
|
private PrecedingData insertCompletedPrecedingData() {
|
|
LocalDateTime now = LocalDateTime.now();
|
|
InstitutionIdentifierApplicationEntity application = new InstitutionIdentifierApplicationEntity();
|
|
application.setBankCode("BKCHCNBJ40001");
|
|
application.setUserId(USER_ID);
|
|
application.setSchoolId(SCHOOL_ID);
|
|
application.setClassId(CLASS_ID);
|
|
application.setRequestTimestamp("20260805180000");
|
|
application.setOriginalText("BKCHCNBJ40001|20260805180000");
|
|
application.setDigest("3A4B5C6D7E8F0000000000000000000000000000000000000000000000000000");
|
|
application.setBankSignature("3044AABB");
|
|
application.setStatus("FEEDBACKED");
|
|
application.setVerificationKeyId(InstitutionKeyService.BANK_SECOND_KEY);
|
|
application.setSignatureValid(true);
|
|
application.setDigestMatches(true);
|
|
application.setSigningKeyId(InstitutionKeyService.CENTRAL_FIRST_KEY);
|
|
application.setInstitutionIdentifier("ORG_3A4B5C6D7E8F");
|
|
application.setCentralBankSignature("3044CCDD");
|
|
application.setTrainingRound(1);
|
|
application.setScoringCriteria(0);
|
|
application.setCreatedAt(now);
|
|
application.setUpdatedAt(now);
|
|
application.setCreatedBy("module-four-tester");
|
|
application.setUpdatedBy("module-four-tester");
|
|
application.setDeleted(false);
|
|
applicationMapper.insert(application);
|
|
|
|
CurrencyGenerationRequestEntity request = new CurrencyGenerationRequestEntity();
|
|
request.setIdentifierApplicationId(application.getId());
|
|
request.setInstitutionIdentifier(application.getInstitutionIdentifier());
|
|
request.setFullInstitutionIdentifier(application.getDigest());
|
|
request.setAmount(new BigDecimal("1589.85"));
|
|
request.setDeliveryNodeCode("SYS_DC_001");
|
|
request.setRequestTimestamp("20260805180100");
|
|
request.setRequestOriginalText("ORG_3A4B5C6D7E8F|1589.85|SYS_DC_001|20260805180100");
|
|
request.setRequestDigest("7A8B9C0D1E2F3A4B5C6D7E8F9A0B1C2D3E4F5A6B7C8D9E0F1A2B3C4D5E6F");
|
|
request.setBankSignature("3044AABB");
|
|
request.setSigningKeyId(InstitutionKeyService.BANK_SECOND_KEY);
|
|
request.setRequestMessage("{request:true}");
|
|
request.setStatus(CurrencyGenerationRequest.Status.RECEIVED.name());
|
|
request.setUserId(USER_ID);
|
|
request.setSchoolId(SCHOOL_ID);
|
|
request.setClassId(CLASS_ID);
|
|
request.setCreatedAt(now);
|
|
request.setUpdatedAt(now);
|
|
request.setCreatedBy("module-four-tester");
|
|
request.setUpdatedBy("module-four-tester");
|
|
request.setDeleted(false);
|
|
requestMapper.insert(request);
|
|
|
|
CurrencyGenerationVerificationEntity verification = new CurrencyGenerationVerificationEntity();
|
|
verification.setRequestId(request.getId());
|
|
verification.setRequestMessage(request.getRequestMessage());
|
|
verification.setRequestOriginalText(request.getRequestOriginalText());
|
|
verification.setRequestDigest(request.getRequestDigest());
|
|
verification.setBankSignature(request.getBankSignature());
|
|
verification.setBankKeyId(request.getSigningKeyId());
|
|
verification.setAmount(request.getAmount());
|
|
verification.setSignatureValid(true);
|
|
verification.setRecomputedDigest(request.getRequestDigest());
|
|
verification.setDigestMatches(true);
|
|
verification.setTotalQuota(new BigDecimal("500000000.00"));
|
|
verification.setUsedQuota(new BigDecimal("495000000.00"));
|
|
verification.setRemainingQuota(new BigDecimal("5000000.00"));
|
|
verification.setQuotaSufficient(true);
|
|
verification.setCentralBankKeyId(InstitutionKeyService.CENTRAL_FIRST_KEY);
|
|
verification.setCentralBankSignature("3044CCDD");
|
|
verification.setConfirmationTime("20260805180200");
|
|
verification.setConfirmationMessage("{confirmed:true}");
|
|
verification.setStatus(CurrencyGenerationVerification.Status.RESPONSE_RETURNED.name());
|
|
verification.setUserId(USER_ID);
|
|
verification.setSchoolId(SCHOOL_ID);
|
|
verification.setClassId(CLASS_ID);
|
|
verification.setCreatedAt(now);
|
|
verification.setUpdatedAt(now);
|
|
verification.setCreatedBy("module-four-tester");
|
|
verification.setUpdatedBy("module-four-tester");
|
|
verification.setDeleted(false);
|
|
verificationMapper.insert(verification);
|
|
return new PrecedingData(application.getId(), request.getId(), verification.getId());
|
|
}
|
|
|
|
private static class PrecedingData {
|
|
private final long applicationId;
|
|
private final long requestId;
|
|
private final long verificationId;
|
|
|
|
private PrecedingData(long applicationId, long requestId, long verificationId) {
|
|
this.applicationId = applicationId;
|
|
this.requestId = requestId;
|
|
this.verificationId = verificationId;
|
|
}
|
|
}
|
|
}
|