chore/issuance-comments-zh
赵佳正 4 weeks ago
commit 9a55f2747f

@ -320,25 +320,25 @@ public class WalletPrerequisiteProjectionService {
}
private List<BankAccount> bankAccounts(String userId, String bankCode) {
return jdbc.query("SELECT account_id,user_id,bank_code,card_number,bank_name,balance,frozen_amount,status " +
return jdbc.query("SELECT account_id,user_id,bank_code,card_number " +
"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.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);
rs.getString("bank_code"), rs.getString("card_number")), 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)
if (!source.userId.equals(value.userId) || !institution.bankCode.equals(value.bankCode)
|| source.application.cardNumber.equals(value.cardNumber)
|| 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.
// Only accounts no downstream experiment has touched follow a re-run module 3 (guarded by the
// NOT EXISTS clauses, which cover every balance/freeze mutation path). A matching card never
// rewrites the row, so runtime balances must not 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 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<>?)",
@ -484,11 +484,9 @@ public class WalletPrerequisiteProjectionService {
private final String userId;
private final String bankCode;
private final String cardNumber;
private final boolean emptyShell;
private BankAccount(String accountId, String userId, String bankCode, String cardNumber, boolean emptyShell) {
private BankAccount(String accountId, String userId, String bankCode, String cardNumber) {
this.accountId = accountId; this.userId = userId;
this.bankCode = bankCode; this.cardNumber = cardNumber;
this.emptyShell = emptyShell;
}
}

@ -168,11 +168,23 @@ class WalletPrerequisiteProjectionServiceTest {
}
@ParameterizedTest
@ValueSource(strings = {"balance=1", "frozen_amount=1", "bank_name='已有开户行'", "status='DISABLED'"})
void nonShellAccountWithDifferentCardIsNeverOverwritten(String state) {
@ValueSource(strings = {"order_history", "debit_history", "status_disabled"})
void accountWithDownstreamHistoryOrDisabledIsNeverOverwritten(String scenario) {
seedCompleted();
String accountId = seedBoundShell();
jdbc.update("UPDATE simulated_bank_account SET " + state + " WHERE account_id=?", accountId);
jdbc.update("UPDATE wallet_application SET input_bank_card_number='6216610100007654321' WHERE user_id=?",
SUBJECT_USER);
if ("order_history".equals(scenario)) {
insertCancelledOrder(accountId, "00000000-0000-0000-0000-000000000890", "PROJECTION_ORDER");
} else if ("debit_history".equals(scenario)) {
insertCancelledOrder(accountId, "00000000-0000-0000-0000-000000000891", "PROJECTION_DEBIT");
jdbc.update("INSERT INTO exchange_debit_record (voucher_no,exchange_id,bank_account_id," +
"before_balance,amount,after_balance,status,created_at,created_by) " +
"VALUES ('VOUCHER_PROJECTION_DEBIT',?,?,50000,1000,49000,'SETTLED'," +
"CURRENT_TIMESTAMP,'test')", "00000000-0000-0000-0000-000000000891", accountId);
} else {
jdbc.update("UPDATE simulated_bank_account SET status='DISABLED' WHERE account_id=?", accountId);
}
assertThatThrownBy(() -> service.ensureForSubject(subject()))
.isInstanceOf(BusinessException.class).hasMessageContaining("共享银行账户与模块三银行卡结果不一致");
@ -180,6 +192,41 @@ class WalletPrerequisiteProjectionServiceTest {
String.class, accountId)).isEqualTo("6222020000000000000");
}
@Test
void retrainedCardOnUnusedProjectedAccountIsOverwrittenInPlace() {
seedCompleted();
service.ensureForSubject(subject());
String accountId = jdbc.queryForObject(
"SELECT account_id FROM simulated_bank_account WHERE user_id=?", String.class, SUBJECT_USER);
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("测试银行");
ExchangeContext context = exchangeResources.loadContext(new ExchangeActor(SUBJECT_USER, "test", SCHOOL_ID, CLASS_ID));
assertThat(context.getBankCardNumber()).isEqualTo("6216610100007654321");
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");
}
private void insertCancelledOrder(String accountId, String orderId, String requestNo) {
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 " +
"(?,?,?,?,?,?,?,1,'CNY','20260904090000','TEST','TEST','TEST','CANCELLED'," +
"CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,'test','test')",
orderId, requestNo, SUBJECT_USER, WALLET_ID, accountId, BANK_CODE, "ORG_EXPECTED");
}
@Test
void evenCancelledExchangeHistoryPreventsShellRestoration() {
seedCompleted();

Loading…
Cancel
Save