diff --git a/src/main/java/com/yau/digitalrmb/exchange/infrastructure/persistence/JdbcExchangeResourceRepository.java b/src/main/java/com/yau/digitalrmb/exchange/infrastructure/persistence/JdbcExchangeResourceRepository.java index 9c0f538..2ba80f7 100644 --- a/src/main/java/com/yau/digitalrmb/exchange/infrastructure/persistence/JdbcExchangeResourceRepository.java +++ b/src/main/java/com/yau/digitalrmb/exchange/infrastructure/persistence/JdbcExchangeResourceRepository.java @@ -109,7 +109,7 @@ public class JdbcExchangeResourceRepository implements ExchangeResourceRepositor @Override public void initializeContext(ExchangeActor actor) { - walletPrerequisites.ensureForSubject(new InstitutionKeySubject( + walletPrerequisites.ensureForModule4(new InstitutionKeySubject( actor.getUserId(), actor.getSchoolId(), actor.getClassId())); } diff --git a/src/main/java/com/yau/digitalrmb/shared/wallet/WalletPrerequisiteProjectionService.java b/src/main/java/com/yau/digitalrmb/shared/wallet/WalletPrerequisiteProjectionService.java index f08e38c..22b94f1 100644 --- a/src/main/java/com/yau/digitalrmb/shared/wallet/WalletPrerequisiteProjectionService.java +++ b/src/main/java/com/yau/digitalrmb/shared/wallet/WalletPrerequisiteProjectionService.java @@ -31,7 +31,23 @@ public class WalletPrerequisiteProjectionService { @Transactional public WalletPrerequisiteReference ensureForSubject(InstitutionKeySubject subject) { - SourceWallet source = loadCompletedSource(subject); + return ensureForSubject(subject, false); + } + + /** + * Module four follows the module-three prototype: step four is complete when + * the commercial bank sends the confirmation whose JSON status is ACTIVATED. + * Older module-five data may still require the central final-confirmation row, + * so this compatibility path is intentionally limited to the exchange module. + */ + @Transactional + public WalletPrerequisiteReference ensureForModule4(InstitutionKeySubject subject) { + return ensureForSubject(subject, true); + } + + private WalletPrerequisiteReference ensureForSubject(InstitutionKeySubject subject, + boolean allowModule3Step4) { + SourceWallet source = loadCompletedSource(subject, allowModule3Step4); Institution institution = loadInstitution(subject); String accountId = ensureBankAccount(source, institution); ensureWallet(source); @@ -53,12 +69,7 @@ public class WalletPrerequisiteProjectionService { @Transactional public WalletPrerequisiteReference ensureForWallet(String walletId) { if (blank(walletId)) throw prerequisite(); - List subjects = jdbc.query( - "SELECT DISTINCT user_id,school_id,class_id FROM central_wallet_activation " + - "WHERE wallet_id=? AND wallet_activated=TRUE AND final_sent=TRUE " + - "AND cb_final_signature IS NOT NULL AND deleted=FALSE", - (rs, row) -> new InstitutionKeySubject(rs.getString("user_id"), - rs.getLong("school_id"), rs.getLong("class_id")), walletId.trim()); + List subjects = findSubjectsForWallet(walletId.trim()); if (subjects.isEmpty()) throw prerequisite(); if (subjects.size() != 1) throw conflict("钱包标识对应多个实验主体"); return ensureForSubject(subjects.get(0)); @@ -67,18 +78,17 @@ public class WalletPrerequisiteProjectionService { @Transactional(readOnly = true) public WalletPrerequisiteReference referenceForWallet(String walletId) { if (blank(walletId)) throw prerequisite(); - List subjects = jdbc.query( - "SELECT DISTINCT user_id,school_id,class_id FROM central_wallet_activation " + - "WHERE wallet_id=? AND wallet_activated=TRUE AND final_sent=TRUE " + - "AND cb_final_signature IS NOT NULL AND deleted=FALSE", - (rs, row) -> new InstitutionKeySubject(rs.getString("user_id"), - rs.getLong("school_id"), rs.getLong("class_id")), walletId.trim()); + List subjects = findSubjectsForWallet(walletId.trim()); if (subjects.isEmpty()) throw prerequisite(); if (subjects.size() != 1) throw conflict("钱包标识对应多个实验主体"); return referenceForSubject(subjects.get(0)); } private SourceWallet loadCompletedSource(InstitutionKeySubject subject) { + return loadCompletedSource(subject, false); + } + + private SourceWallet loadCompletedSource(InstitutionKeySubject subject, boolean allowModule3Step4) { List activations = jdbc.query( "SELECT wallet_id,cb_final_signature,final_time FROM central_wallet_activation " + "WHERE user_id=? AND school_id=? AND class_id=? AND wallet_activated=TRUE " + @@ -87,8 +97,33 @@ public class WalletPrerequisiteProjectionService { (rs, row) -> new Activation(rs.getString("wallet_id"), rs.getString("cb_final_signature"), rs.getString("final_time")), subject.getUserId(), subject.getSchoolId(), subject.getClassId()); - if (activations.isEmpty()) throw prerequisite(); - Activation activation = activations.get(0); + Activation activation; + if (!activations.isEmpty()) { + // Prefer the complete step-five record when it exists. It contains the + // central bank's final confirmation signature used by the shared wallet. + activation = activations.get(0); + } else if (allowModule3Step4) { + // The prototype finishes module three step four when the commercial + // bank sends the activation confirmation. The confirmation JSON itself + // carries status=ACTIVATED, while older deployments do not materialize + // a matching central_wallet_activation row. Use that sent confirmation + // as the module-four source instead of requiring an invisible central-bank + // action that is not present in the prototype. + List sentConfirmations = jdbc.query( + "SELECT wallet_id,activate_signature,activate_timestamp " + + "FROM smart_contract_generation " + + "WHERE user_id=? AND school_id=? AND class_id=? AND sent=TRUE " + + "AND status='SENT' AND confirm_json LIKE '%ACTIVATED%' " + + "AND activate_signature IS NOT NULL AND activate_timestamp IS NOT NULL " + + "AND deleted=FALSE ORDER BY created_at DESC LIMIT 1", + (rs, row) -> new Activation(rs.getString("wallet_id"), + rs.getString("activate_signature"), rs.getString("activate_timestamp")), + subject.getUserId(), subject.getSchoolId(), subject.getClassId()); + if (sentConfirmations.isEmpty()) throw prerequisite(); + activation = sentConfirmations.get(0); + } else { + throw prerequisite(); + } List applications = jdbc.query( "SELECT COALESCE(account_bank,selected_bank) bank_name," + @@ -147,6 +182,15 @@ public class WalletPrerequisiteProjectionService { certificates.get(0), rootSignatures.get(0), contract); } + private List findSubjectsForWallet(String walletId) { + return jdbc.query( + "SELECT DISTINCT user_id,school_id,class_id FROM central_wallet_activation " + + "WHERE wallet_id=? AND wallet_activated=TRUE AND final_sent=TRUE " + + "AND cb_final_signature IS NOT NULL AND deleted=FALSE", + (rs, row) -> new InstitutionKeySubject(rs.getString("user_id"), + rs.getLong("school_id"), rs.getLong("class_id")), walletId); + } + private Institution loadInstitution(InstitutionKeySubject subject) { List institutions = jdbc.query( "SELECT bank_code,institution_identifier FROM institution_identifier_application " +