Completed exchanges keep the shared wallet projection pinned to the old
module 3 result and the cancel guard refuses to roll the coins back, so a
retrained card could never reach modules 4 and 5. The new endpoint wipes
that user's exchange and payment history, returns the coins to the
commercial bank vault, and re-projects from the latest module 3 result in
the same transaction so the read-only context endpoints stay usable.
Module 3 step 1 (output application) had no length limit on the bank
card number fields in either the frontend input or the backend DTO.
A long test value overflowed VARCHAR(30) and the MySQL update failed
with 'Data truncation: Data too long for column bank_card_number',
returning a 500 and blocking the whole module 3 flow (and therefore
module 4/5 downstream).
- widen wallet_application.bank_card_number/input_bank_card_number,
bank_received_wallet_application.bank_card_number,
wallet_registration_request.bank_card_number, and
central_wallet_registration.bank_card_number from VARCHAR(30) to
VARCHAR(64) in schema.sql (already applied directly to the running
dev database via ALTER TABLE)
- add @Size(max = 64) to WalletApplicationRequest's card number fields
- add maxlength=64 to the bank card input on the wallet open step
Module 3 retraining projects a new wallet_id/signature via
loadCompletedSource(), but ensureWallet() only ever validated the
existing digital_wallet row against it and threw a conflict when the
stale row (from before retraining) didn't match. If that stale wallet
was never actually used downstream (zero balance/frozen amount, no
exchange/payment/ledger history), retire it and its dependent
certificate/contract/binding rows so the fresh module 3 result can be
projected instead of surfacing '共享钱包与模块三最终结果不一致'.
Wallets that were actually used still correctly conflict.