feat:文件上传的功能开发

chore/issuance-comments-zh
赵佳正 1 month ago
parent d5daf546ab
commit b9c9cece30

@ -169,28 +169,22 @@ public class WalletPrerequisiteProjectionService {
(source.userId + "|" + institution.bankCode).getBytes(StandardCharsets.UTF_8)); (source.userId + "|" + institution.bankCode).getBytes(StandardCharsets.UTF_8));
List<BankAccount> existing = bankAccounts(source.userId, institution.bankCode); List<BankAccount> existing = bankAccounts(source.userId, institution.bankCode);
if (!existing.isEmpty()) { if (!existing.isEmpty()) {
validateAccount(existing.get(0), source, institution); restoreUnusedBankAccount(existing.get(0), source, institution);
BigDecimal expectedBalance = source.application.accountBalance != null validateAccount(bankAccounts(source.userId, institution.bankCode).get(0), source, institution);
&& source.application.accountBalance.compareTo(BigDecimal.ZERO) > 0
? source.application.accountBalance : new BigDecimal("50000.00");
jdbc.update("UPDATE simulated_bank_account SET balance=? WHERE account_id=? AND balance=0",
expectedBalance, existing.get(0).accountId);
return existing.get(0).accountId; return existing.get(0).accountId;
} }
BigDecimal initialBalance = source.application.accountBalance != null
&& source.application.accountBalance.compareTo(BigDecimal.ZERO) > 0
? source.application.accountBalance : new BigDecimal("50000.00");
try { try {
jdbc.update("INSERT INTO simulated_bank_account " + jdbc.update("INSERT INTO simulated_bank_account " +
"(account_id,user_id,bank_code,bank_name,card_number,card_last4,balance,frozen_amount,status,created_at,updated_at) " + "(account_id,user_id,bank_code,bank_name,card_number,card_last4,balance,frozen_amount,status,created_at,updated_at) " +
"VALUES (?,?,?,?,?,?,?,0,'ACTIVE',CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)", "VALUES (?,?,?,?,?,?,?,0,'ACTIVE',CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)",
expectedId, source.userId, institution.bankCode, source.application.bankName, expectedId, source.userId, institution.bankCode, source.application.bankName,
source.application.cardNumber, lastFour(source.application.cardNumber), source.application.cardNumber, lastFour(source.application.cardNumber),
initialBalance); source.application.accountBalance);
} catch (DuplicateKeyException exception) { } catch (DuplicateKeyException exception) {
existing = bankAccounts(source.userId, institution.bankCode); existing = bankAccounts(source.userId, institution.bankCode);
if (existing.isEmpty()) throw exception; if (existing.isEmpty()) throw exception;
validateAccount(existing.get(0), source, institution); restoreUnusedBankAccount(existing.get(0), source, institution);
validateAccount(bankAccounts(source.userId, institution.bankCode).get(0), source, institution);
return existing.get(0).accountId; return existing.get(0).accountId;
} }
return expectedId; return expectedId;
@ -299,10 +293,31 @@ public class WalletPrerequisiteProjectionService {
} }
private List<BankAccount> bankAccounts(String userId, String bankCode) { private List<BankAccount> bankAccounts(String userId, String bankCode) {
return jdbc.query("SELECT account_id,user_id,bank_code,card_number FROM simulated_bank_account " + return jdbc.query("SELECT account_id,user_id,bank_code,card_number,bank_name,balance,frozen_amount,status " +
"WHERE user_id=? AND bank_code=?", "FROM simulated_bank_account WHERE user_id=? AND bank_code=? FOR UPDATE",
(rs, row) -> new BankAccount(rs.getString("account_id"), rs.getString("user_id"), (rs, row) -> new BankAccount(rs.getString("account_id"), rs.getString("user_id"),
rs.getString("bank_code"), rs.getString("card_number")), userId, bankCode); rs.getString("bank_code"), rs.getString("card_number"),
blank(rs.getString("bank_name")) && rs.getBigDecimal("balance").signum() == 0
&& rs.getBigDecimal("frozen_amount").signum() == 0
&& "ACTIVE".equals(rs.getString("status"))), userId, bankCode);
}
private void restoreUnusedBankAccount(BankAccount value, SourceWallet source, Institution institution) {
if (!value.emptyShell || !source.userId.equals(value.userId)
|| !institution.bankCode.equals(value.bankCode) || blank(source.application.bankName)
|| source.application.accountBalance.signum() < 0) {
return;
}
// Repair only unused legacy shells. Runtime balances must never be reset from an opening snapshot.
jdbc.update("UPDATE simulated_bank_account SET bank_name=?,card_number=?,card_last4=?,balance=?," +
"updated_at=CURRENT_TIMESTAMP WHERE account_id=? AND user_id=? AND bank_code=? " +
"AND TRIM(COALESCE(bank_name,''))='' AND balance=0 AND frozen_amount=0 AND status='ACTIVE' " +
"AND NOT EXISTS (SELECT 1 FROM currency_exchange_order WHERE bank_account_id=?) " +
"AND NOT EXISTS (SELECT 1 FROM exchange_debit_record WHERE bank_account_id=?) " +
"AND NOT EXISTS (SELECT 1 FROM wallet_bank_binding WHERE bank_account_id=? AND wallet_id<>?)",
source.application.bankName, source.application.cardNumber, lastFour(source.application.cardNumber),
source.application.accountBalance, value.accountId, source.userId, institution.bankCode,
value.accountId, value.accountId, value.accountId, source.walletId);
} }
private void validateAccount(BankAccount value, SourceWallet source, Institution institution) { private void validateAccount(BankAccount value, SourceWallet source, Institution institution) {
@ -442,9 +457,11 @@ public class WalletPrerequisiteProjectionService {
private final String userId; private final String userId;
private final String bankCode; private final String bankCode;
private final String cardNumber; private final String cardNumber;
private BankAccount(String accountId, String userId, String bankCode, String cardNumber) { private final boolean emptyShell;
private BankAccount(String accountId, String userId, String bankCode, String cardNumber, boolean emptyShell) {
this.accountId = accountId; this.userId = userId; this.accountId = accountId; this.userId = userId;
this.bankCode = bankCode; this.cardNumber = cardNumber; this.bankCode = bankCode; this.cardNumber = cardNumber;
this.emptyShell = emptyShell;
} }
} }

@ -0,0 +1,24 @@
package com.yau.digitalrmb.walletopening.application;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ApiModel(description = "开通数字货币钱包实验重新实训结果")
public class WalletOpeningTrainingResetResult {
@ApiModelProperty(value = "是否重置成功", example = "true")
private final boolean reset;
@ApiModelProperty(value = "逻辑删除的记录总数", example = "12")
private final int deletedRecordCount;
@ApiModelProperty(value = "处理说明")
private final String message;
public WalletOpeningTrainingResetResult(int deletedRecordCount) {
this.reset = true;
this.deletedRecordCount = deletedRecordCount;
this.message = "钱包开通实验步骤数据已逻辑删除,可从步骤一重新开始";
}
public boolean isReset() { return reset; }
public int getDeletedRecordCount() { return deletedRecordCount; }
public String getMessage() { return message; }
}

@ -0,0 +1,126 @@
package com.yau.digitalrmb.walletopening.application;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.yau.digitalrmb.institutionidentity.application.InstitutionKeySubject;
import com.yau.digitalrmb.institutionidentity.infrastructure.StuModuleScoreDetailsEntity;
import com.yau.digitalrmb.institutionidentity.infrastructure.StuModuleScoreDetailsMapper;
import com.yau.digitalrmb.walletopening.infrastructure.BankReceivedApplicationEntity;
import com.yau.digitalrmb.walletopening.infrastructure.BankReceivedApplicationMapper;
import com.yau.digitalrmb.walletopening.infrastructure.CentralWalletActivationEntity;
import com.yau.digitalrmb.walletopening.infrastructure.CentralWalletActivationMapper;
import com.yau.digitalrmb.walletopening.infrastructure.CentralWalletRegistrationEntity;
import com.yau.digitalrmb.walletopening.infrastructure.CentralWalletRegistrationMapper;
import com.yau.digitalrmb.walletopening.infrastructure.SmartContractGenerationEntity;
import com.yau.digitalrmb.walletopening.infrastructure.SmartContractGenerationMapper;
import com.yau.digitalrmb.walletopening.infrastructure.WalletApplicationEntity;
import com.yau.digitalrmb.walletopening.infrastructure.WalletApplicationMapper;
import com.yau.digitalrmb.walletopening.infrastructure.WalletIdentifierGenerationEntity;
import com.yau.digitalrmb.walletopening.infrastructure.WalletIdentifierGenerationMapper;
import com.yau.digitalrmb.walletopening.infrastructure.WalletRegistrationRequestEntity;
import com.yau.digitalrmb.walletopening.infrastructure.WalletRegistrationRequestMapper;
import com.yau.digitalrmb.walletopening.infrastructure.WalletVerificationEntity;
import com.yau.digitalrmb.walletopening.infrastructure.WalletVerificationMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
@Service
public class WalletOpeningTrainingResetService {
@Resource private WalletApplicationMapper walletApplicationMapper;
@Resource private BankReceivedApplicationMapper bankReceivedApplicationMapper;
@Resource private WalletIdentifierGenerationMapper walletIdentifierGenerationMapper;
@Resource private SmartContractGenerationMapper smartContractGenerationMapper;
@Resource private WalletRegistrationRequestMapper walletRegistrationRequestMapper;
@Resource private WalletVerificationMapper walletVerificationMapper;
@Resource private CentralWalletRegistrationMapper centralWalletRegistrationMapper;
@Resource private CentralWalletActivationMapper centralWalletActivationMapper;
@Resource private StuModuleScoreDetailsMapper scoreDetailsMapper;
@Transactional
public WalletOpeningTrainingResetResult reset(InstitutionKeySubject subject) {
int count = 0;
count += logicalDeleteWalletApplications(subject);
count += logicalDeleteBankReceivedApplications(subject);
count += logicalDeleteWalletIdentifierGenerations(subject);
count += logicalDeleteSmartContracts(subject);
count += logicalDeleteWalletRegistrationRequests(subject);
count += logicalDeleteWalletVerifications(subject);
count += logicalDeleteCentralWalletRegistrations(subject);
count += logicalDeleteCentralWalletActivations(subject);
count += logicalDeleteScore(subject.getUserId());
return new WalletOpeningTrainingResetResult(count);
}
private int logicalDeleteWalletApplications(InstitutionKeySubject s) {
return walletApplicationMapper.update(null, new LambdaUpdateWrapper<WalletApplicationEntity>()
.eq(WalletApplicationEntity::getUserId, s.getUserId())
.eq(WalletApplicationEntity::getSchoolId, s.getSchoolId())
.eq(WalletApplicationEntity::getClassId, s.getClassId())
.set(WalletApplicationEntity::getDeleted, true));
}
private int logicalDeleteBankReceivedApplications(InstitutionKeySubject s) {
return bankReceivedApplicationMapper.update(null, new LambdaUpdateWrapper<BankReceivedApplicationEntity>()
.eq(BankReceivedApplicationEntity::getUserId, s.getUserId())
.eq(BankReceivedApplicationEntity::getSchoolId, s.getSchoolId())
.eq(BankReceivedApplicationEntity::getClassId, s.getClassId())
.set(BankReceivedApplicationEntity::getDeleted, true));
}
private int logicalDeleteWalletIdentifierGenerations(InstitutionKeySubject s) {
return walletIdentifierGenerationMapper.update(null, new LambdaUpdateWrapper<WalletIdentifierGenerationEntity>()
.eq(WalletIdentifierGenerationEntity::getUserId, s.getUserId())
.eq(WalletIdentifierGenerationEntity::getSchoolId, s.getSchoolId())
.eq(WalletIdentifierGenerationEntity::getClassId, s.getClassId())
.set(WalletIdentifierGenerationEntity::getDeleted, true));
}
private int logicalDeleteSmartContracts(InstitutionKeySubject s) {
return smartContractGenerationMapper.update(null, new LambdaUpdateWrapper<SmartContractGenerationEntity>()
.eq(SmartContractGenerationEntity::getUserId, s.getUserId())
.eq(SmartContractGenerationEntity::getSchoolId, s.getSchoolId())
.eq(SmartContractGenerationEntity::getClassId, s.getClassId())
.set(SmartContractGenerationEntity::getDeleted, true));
}
private int logicalDeleteWalletRegistrationRequests(InstitutionKeySubject s) {
return walletRegistrationRequestMapper.update(null, new LambdaUpdateWrapper<WalletRegistrationRequestEntity>()
.eq(WalletRegistrationRequestEntity::getUserId, s.getUserId())
.eq(WalletRegistrationRequestEntity::getSchoolId, s.getSchoolId())
.eq(WalletRegistrationRequestEntity::getClassId, s.getClassId())
.set(WalletRegistrationRequestEntity::getDeleted, true));
}
private int logicalDeleteWalletVerifications(InstitutionKeySubject s) {
return walletVerificationMapper.update(null, new LambdaUpdateWrapper<WalletVerificationEntity>()
.eq(WalletVerificationEntity::getUserId, s.getUserId())
.eq(WalletVerificationEntity::getSchoolId, s.getSchoolId())
.eq(WalletVerificationEntity::getClassId, s.getClassId())
.set(WalletVerificationEntity::getDeleted, true));
}
private int logicalDeleteCentralWalletRegistrations(InstitutionKeySubject s) {
return centralWalletRegistrationMapper.update(null, new LambdaUpdateWrapper<CentralWalletRegistrationEntity>()
.eq(CentralWalletRegistrationEntity::getUserId, s.getUserId())
.eq(CentralWalletRegistrationEntity::getSchoolId, s.getSchoolId())
.eq(CentralWalletRegistrationEntity::getClassId, s.getClassId())
.set(CentralWalletRegistrationEntity::getDeleted, true));
}
private int logicalDeleteCentralWalletActivations(InstitutionKeySubject s) {
return centralWalletActivationMapper.update(null, new LambdaUpdateWrapper<CentralWalletActivationEntity>()
.eq(CentralWalletActivationEntity::getUserId, s.getUserId())
.eq(CentralWalletActivationEntity::getSchoolId, s.getSchoolId())
.eq(CentralWalletActivationEntity::getClassId, s.getClassId())
.set(CentralWalletActivationEntity::getDeleted, true));
}
private int logicalDeleteScore(String userId) {
return scoreDetailsMapper.update(null, new LambdaUpdateWrapper<StuModuleScoreDetailsEntity>()
.eq(StuModuleScoreDetailsEntity::getUserId, userId)
.eq(StuModuleScoreDetailsEntity::getMoudule, WalletOpeningScoreService.MODULE_NAME)
.set(StuModuleScoreDetailsEntity::getDeleted, true));
}
}

@ -0,0 +1,38 @@
package com.yau.digitalrmb.walletopening.interfaces.rest;
import com.yau.digitalrmb.institutionidentity.application.InstitutionKeySubject;
import com.yau.digitalrmb.security.context.AuthContextHolder;
import com.yau.digitalrmb.security.context.JwtUser;
import com.yau.digitalrmb.shared.api.ApiResponse;
import com.yau.digitalrmb.shared.web.TraceIdFilter;
import com.yau.digitalrmb.walletopening.application.WalletOpeningTrainingResetResult;
import com.yau.digitalrmb.walletopening.application.WalletOpeningTrainingResetService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.slf4j.MDC;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping("/api/v1/wallet-opening/training")
@Tag(name = "开通数字货币钱包-实验管理", description = "钱包开通实验重新实训")
public class WalletOpeningTrainingResetController {
@Resource
private WalletOpeningTrainingResetService resetService;
@PostMapping("/reset")
@Operation(summary = "重新开始钱包开通实验",
description = "逻辑删除当前用户在钱包开通模块的全部步骤数据(申请记录、钱包标识、智能合约、注册登记、验证记录、央行注册与激活、成绩明细),可从步骤一重新开始。")
public ApiResponse<WalletOpeningTrainingResetResult> reset() {
JwtUser jwtUser = AuthContextHolder.get();
return ApiResponse.success(resetService.reset(InstitutionKeySubject.from(jwtUser)), traceId());
}
private String traceId() {
return MDC.get(TraceIdFilter.MDC_KEY);
}
}

@ -3,11 +3,16 @@ package com.yau.digitalrmb.shared.wallet;
import com.yau.digitalrmb.institutionidentity.application.InstitutionKeySubject; import com.yau.digitalrmb.institutionidentity.application.InstitutionKeySubject;
import com.yau.digitalrmb.institutionidentity.domain.InstitutionIdentityCryptography; import com.yau.digitalrmb.institutionidentity.domain.InstitutionIdentityCryptography;
import com.yau.digitalrmb.institutionidentity.domain.InstitutionSm2KeyPair; import com.yau.digitalrmb.institutionidentity.domain.InstitutionSm2KeyPair;
import com.yau.digitalrmb.exchange.domain.model.ExchangeActor;
import com.yau.digitalrmb.exchange.domain.model.ExchangeContext;
import com.yau.digitalrmb.exchange.domain.repository.ExchangeResourceRepository;
import com.yau.digitalrmb.shared.exception.BusinessException; import com.yau.digitalrmb.shared.exception.BusinessException;
import com.yau.digitalrmb.testsupport.WalletOpeningTestData; import com.yau.digitalrmb.testsupport.WalletOpeningTestData;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
@ -33,6 +38,7 @@ class WalletPrerequisiteProjectionServiceTest {
@Autowired private JdbcTemplate jdbc; @Autowired private JdbcTemplate jdbc;
@Autowired private InstitutionIdentityCryptography cryptography; @Autowired private InstitutionIdentityCryptography cryptography;
@Autowired private WalletPrerequisiteProjectionService service; @Autowired private WalletPrerequisiteProjectionService service;
@Autowired private ExchangeResourceRepository exchangeResources;
private InstitutionSm2KeyPair keyPair; private InstitutionSm2KeyPair keyPair;
@ -109,6 +115,7 @@ class WalletPrerequisiteProjectionServiceTest {
service.ensureForSubject(subject()); service.ensureForSubject(subject());
jdbc.update("UPDATE digital_wallet SET balance=321.00,frozen_amount=20.00 WHERE wallet_id=?", WALLET_ID); jdbc.update("UPDATE digital_wallet SET balance=321.00,frozen_amount=20.00 WHERE wallet_id=?", WALLET_ID);
jdbc.update("UPDATE wallet_contract SET daily_used_amount=45.00 WHERE wallet_id=?", WALLET_ID); jdbc.update("UPDATE wallet_contract SET daily_used_amount=45.00 WHERE wallet_id=?", WALLET_ID);
jdbc.update("UPDATE simulated_bank_account SET balance=123.00,frozen_amount=10.00 WHERE user_id=?", SUBJECT_USER);
Timestamp activationUpdatedAt = jdbc.queryForObject( Timestamp activationUpdatedAt = jdbc.queryForObject(
"SELECT updated_at FROM central_wallet_activation WHERE id=?", Timestamp.class, ACTIVATION_ID); "SELECT updated_at FROM central_wallet_activation WHERE id=?", Timestamp.class, ACTIVATION_ID);
@ -122,6 +129,125 @@ class WalletPrerequisiteProjectionServiceTest {
BigDecimal.class, WALLET_ID)).isEqualByComparingTo("45.00"); BigDecimal.class, WALLET_ID)).isEqualByComparingTo("45.00");
assertThat(jdbc.queryForObject("SELECT updated_at FROM central_wallet_activation WHERE id=?", assertThat(jdbc.queryForObject("SELECT updated_at FROM central_wallet_activation WHERE id=?",
Timestamp.class, ACTIVATION_ID)).isEqualTo(activationUpdatedAt); Timestamp.class, ACTIVATION_ID)).isEqualTo(activationUpdatedAt);
assertThat(jdbc.queryForObject("SELECT balance FROM simulated_bank_account WHERE user_id=?",
BigDecimal.class, SUBJECT_USER)).isEqualByComparingTo("123.00");
assertThat(jdbc.queryForObject("SELECT frozen_amount FROM simulated_bank_account WHERE user_id=?",
BigDecimal.class, SUBJECT_USER)).isEqualByComparingTo("10.00");
}
@Test
void unusedLegacyShellIsRestoredInPlaceFromCompletedModuleThreeAndOnlyOnce() {
seedCompleted();
String accountId = seedBoundShell();
jdbc.update("UPDATE wallet_application SET input_bank_card_number='6216610100007654321' WHERE user_id=?",
SUBJECT_USER);
service.ensureForSubject(subject());
assertThat(jdbc.queryForObject("SELECT card_number FROM simulated_bank_account WHERE account_id=?",
String.class, accountId)).isEqualTo("6216610100007654321");
assertThat(jdbc.queryForObject("SELECT card_last4 FROM simulated_bank_account WHERE account_id=?",
String.class, accountId)).isEqualTo("4321");
assertThat(jdbc.queryForObject("SELECT bank_name FROM simulated_bank_account WHERE account_id=?",
String.class, accountId)).isEqualTo("测试银行");
assertThat(jdbc.queryForObject("SELECT balance FROM simulated_bank_account WHERE account_id=?",
BigDecimal.class, accountId)).isEqualByComparingTo("50000.00");
assertThat(jdbc.queryForObject("SELECT bank_account_id FROM wallet_bank_binding WHERE wallet_id=?",
String.class, WALLET_ID)).isEqualTo(accountId);
ExchangeContext context = exchangeResources.loadContext(new ExchangeActor(SUBJECT_USER, "test", SCHOOL_ID, CLASS_ID));
assertThat(context.getBankAccountId()).isEqualTo(accountId);
assertThat(context.getBankCardNumber()).isEqualTo("6216610100007654321");
assertThat(context.getBankCardLast4()).isEqualTo("4321");
assertThat(context.getBankName()).isEqualTo("测试银行");
assertThat(context.getBankAccountBalance()).isEqualByComparingTo("50000.00");
jdbc.update("UPDATE simulated_bank_account SET balance=49000.00 WHERE account_id=?", accountId);
service.ensureForSubject(subject());
assertThat(jdbc.queryForObject("SELECT balance FROM simulated_bank_account WHERE account_id=?",
BigDecimal.class, accountId)).isEqualByComparingTo("49000.00");
}
@ParameterizedTest
@ValueSource(strings = {"balance=1", "frozen_amount=1", "bank_name='已有开户行'", "status='DISABLED'"})
void nonShellAccountWithDifferentCardIsNeverOverwritten(String state) {
seedCompleted();
String accountId = seedBoundShell();
jdbc.update("UPDATE simulated_bank_account SET " + state + " WHERE account_id=?", accountId);
assertThatThrownBy(() -> service.ensureForSubject(subject()))
.isInstanceOf(BusinessException.class).hasMessageContaining("共享银行账户与模块三银行卡结果不一致");
assertThat(jdbc.queryForObject("SELECT card_number FROM simulated_bank_account WHERE account_id=?",
String.class, accountId)).isEqualTo("6222020000000000000");
}
@Test
void evenCancelledExchangeHistoryPreventsShellRestoration() {
seedCompleted();
String accountId = seedBoundShell();
jdbc.update("INSERT INTO currency_exchange_order (id,request_no,user_id,wallet_id,bank_account_id,bank_code," +
"organization_id,amount,currency,request_timestamp,message_text,signing_original_text,digest," +
"status,created_at,updated_at,created_by,updated_by) VALUES " +
"('00000000-0000-0000-0000-000000000880','PROJECTION_HISTORY',?,?,?,?," +
"'ORG_EXPECTED',1,'CNY','20260904090000','TEST','TEST','TEST','CANCELLED'," +
"CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,'test','test')",
SUBJECT_USER, WALLET_ID, accountId, BANK_CODE);
assertThatThrownBy(() -> service.ensureForSubject(subject()))
.isInstanceOf(BusinessException.class).hasMessageContaining("共享银行账户与模块三银行卡结果不一致");
assertThat(jdbc.queryForObject("SELECT balance FROM simulated_bank_account WHERE account_id=?",
BigDecimal.class, accountId)).isEqualByComparingTo("0");
}
@Test
void laterWalletConflictRollsBackShellRestoration() {
seedCompleted();
String accountId = seedBoundShell();
jdbc.update("UPDATE digital_wallet SET central_bank_confirmation_signature='OTHER' WHERE wallet_id=?", WALLET_ID);
assertThatThrownBy(() -> service.ensureForSubject(subject()))
.isInstanceOf(BusinessException.class).hasMessageContaining("共享钱包与模块三最终结果不一致");
assertThat(jdbc.queryForObject("SELECT card_number FROM simulated_bank_account WHERE account_id=?",
String.class, accountId)).isEqualTo("6222020000000000000");
assertThat(jdbc.queryForObject("SELECT balance FROM simulated_bank_account WHERE account_id=?",
BigDecimal.class, accountId)).isEqualByComparingTo("0");
}
@Test
void incompleteModuleThreeCannotRestoreExistingShell() {
seedCompleted();
String accountId = seedBoundShell();
jdbc.update("UPDATE central_wallet_activation SET final_sent=FALSE WHERE id=?", ACTIVATION_ID);
assertThatThrownBy(() -> service.ensureForSubject(subject()))
.isInstanceOf(BusinessException.class).hasMessageContaining("个人数字钱包开通");
assertThat(jdbc.queryForObject("SELECT bank_name FROM simulated_bank_account WHERE account_id=?",
String.class, accountId)).isEmpty();
}
@Test
void accountBoundToAnotherWalletCannotBeRestored() {
seedCompleted();
String accountId = seedBoundShell();
jdbc.update("INSERT INTO digital_wallet (wallet_id,user_id,wallet_type,status,balance,frozen_amount," +
"central_bank_confirmation_signature,opened_at,created_at,updated_at) VALUES " +
"('WALLET_PROJECTION_OTHER',?,'TYPE_II','ACTIVE',0,0,'OTHER'," +
"CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)", OTHER_USER);
try {
jdbc.update("INSERT INTO wallet_bank_binding (wallet_id,bank_account_id,status,bound_at) " +
"VALUES ('WALLET_PROJECTION_OTHER',?,'BOUND',CURRENT_TIMESTAMP)", accountId);
assertThatThrownBy(() -> service.ensureForSubject(subject()))
.isInstanceOf(BusinessException.class).hasMessageContaining("共享银行账户与模块三银行卡结果不一致");
} finally {
jdbc.update("DELETE FROM wallet_bank_binding WHERE wallet_id='WALLET_PROJECTION_OTHER'");
jdbc.update("DELETE FROM digital_wallet WHERE wallet_id='WALLET_PROJECTION_OTHER'");
}
}
private String seedBoundShell() {
service.ensureForSubject(subject());
jdbc.update("UPDATE simulated_bank_account SET bank_name='',card_number='6222020000000000000'," +
"card_last4='0000',balance=0,frozen_amount=0 WHERE user_id=?", SUBJECT_USER);
return jdbc.queryForObject("SELECT account_id FROM simulated_bank_account WHERE user_id=?", String.class, SUBJECT_USER);
} }
@Test @Test
@ -159,6 +285,8 @@ class WalletPrerequisiteProjectionServiceTest {
} }
private void clean() { private void clean() {
jdbc.update("DELETE FROM exchange_debit_record WHERE exchange_id IN (SELECT id FROM currency_exchange_order WHERE user_id=?)", SUBJECT_USER);
jdbc.update("DELETE FROM currency_exchange_order WHERE user_id=?", SUBJECT_USER);
jdbc.update("DELETE FROM wallet_bank_binding WHERE wallet_id=?", WALLET_ID); jdbc.update("DELETE FROM wallet_bank_binding WHERE wallet_id=?", WALLET_ID);
jdbc.update("DELETE FROM wallet_contract WHERE wallet_id=?", WALLET_ID); jdbc.update("DELETE FROM wallet_contract WHERE wallet_id=?", WALLET_ID);
jdbc.update("DELETE FROM wallet_certificate WHERE wallet_id=?", WALLET_ID); jdbc.update("DELETE FROM wallet_certificate WHERE wallet_id=?", WALLET_ID);

Loading…
Cancel
Save