fix: add teaching payment payee fallback
parent
718755f8a0
commit
aea498726c
@ -0,0 +1,19 @@
|
|||||||
|
package com.yau.digitalrmb.institutionidentity.interfaces.dto;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class CurrencyRuleRequest {
|
||||||
|
@NotBlank(message = "币串前缀不能为空")
|
||||||
|
private String prefix;
|
||||||
|
|
||||||
|
@NotBlank(message = "币串ID规则不能为空")
|
||||||
|
private String idRule;
|
||||||
|
|
||||||
|
@NotBlank(message = "币串初始状态不能为空")
|
||||||
|
private String currencyStatus;
|
||||||
|
}
|
||||||
@ -0,0 +1,189 @@
|
|||||||
|
package com.yau.digitalrmb.payment.infrastructure.persistence;
|
||||||
|
|
||||||
|
import com.yau.digitalrmb.institutionidentity.domain.InstitutionIdentityCryptography;
|
||||||
|
import com.yau.digitalrmb.institutionidentity.domain.InstitutionSm2KeyPair;
|
||||||
|
import com.yau.digitalrmb.shared.api.ErrorCode;
|
||||||
|
import com.yau.digitalrmb.shared.exception.BusinessException;
|
||||||
|
import com.yau.digitalrmb.shared.wallet.WalletPrerequisiteReference;
|
||||||
|
import com.yau.digitalrmb.shared.wallet.WalletPrivateKeyCipher;
|
||||||
|
import org.springframework.dao.DuplicateKeyException;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the real, backend-managed receiving wallets used by the payment
|
||||||
|
* training when a learner has no second account to act as the payee.
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class TeachingPaymentPayeeWhitelist {
|
||||||
|
private static final String BANK_A = "BKCHCNBJ00001";
|
||||||
|
private static final String BANK_B = "BKCHCNBJ00002";
|
||||||
|
private static final String PAYEE_A_USER = "00000000-0000-0000-0000-000000000951";
|
||||||
|
private static final String PAYEE_B_USER = "00000000-0000-0000-0000-000000000952";
|
||||||
|
private static final String PAYEE_A_WALLET = "WALLET_TRAINING_PAYEE_A";
|
||||||
|
private static final String PAYEE_B_WALLET = "WALLET_TRAINING_PAYEE_B";
|
||||||
|
|
||||||
|
private final JdbcTemplate jdbc;
|
||||||
|
private final InstitutionIdentityCryptography cryptography;
|
||||||
|
private final WalletPrivateKeyCipher walletCipher;
|
||||||
|
|
||||||
|
public TeachingPaymentPayeeWhitelist(JdbcTemplate jdbc, InstitutionIdentityCryptography cryptography,
|
||||||
|
WalletPrivateKeyCipher walletCipher) {
|
||||||
|
this.jdbc = jdbc;
|
||||||
|
this.cryptography = cryptography;
|
||||||
|
this.walletCipher = walletCipher;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public WalletPrerequisiteReference resolveForDifferentBank(String payerBankCode) {
|
||||||
|
PayeeDefinition payee = BANK_B.equals(payerBankCode)
|
||||||
|
? new PayeeDefinition(PAYEE_A_USER, PAYEE_A_WALLET, BANK_A, "教学商业银行A", "ORG_TRAINING_PAYEE_A")
|
||||||
|
: new PayeeDefinition(PAYEE_B_USER, PAYEE_B_WALLET, BANK_B, "教学商业银行B", "ORG_TRAINING_PAYEE_B");
|
||||||
|
ensureWallet(payee);
|
||||||
|
ensureCertificate(payee);
|
||||||
|
ensureBankAccount(payee);
|
||||||
|
ensureBinding(payee);
|
||||||
|
return new WalletPrerequisiteReference(payee.userId, 0L, 0L, payee.walletId,
|
||||||
|
payee.bankCode, payee.organizationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureWallet(PayeeDefinition payee) {
|
||||||
|
List<WalletRow> wallets = jdbc.query("SELECT user_id,wallet_type,status FROM digital_wallet WHERE wallet_id=?",
|
||||||
|
(rs, row) -> new WalletRow(rs.getString("user_id"), rs.getString("wallet_type"),
|
||||||
|
rs.getString("status")), payee.walletId);
|
||||||
|
if (wallets.isEmpty()) {
|
||||||
|
try {
|
||||||
|
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 (?,?, 'TYPE_II','ACTIVE',0,0,'TEACHING_PAYEE_CONFIRMED',CURRENT_TIMESTAMP," +
|
||||||
|
"CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)", payee.walletId, payee.userId);
|
||||||
|
} catch (DuplicateKeyException exception) {
|
||||||
|
ensureWallet(payee);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
WalletRow wallet = wallets.get(0);
|
||||||
|
if (!payee.userId.equals(wallet.userId) || !"TYPE_II".equals(wallet.walletType)
|
||||||
|
|| !"ACTIVE".equals(wallet.status)) {
|
||||||
|
throw conflict("教学收款方钱包状态或归属异常");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureCertificate(PayeeDefinition payee) {
|
||||||
|
List<CertificateRow> certificates = jdbc.query("SELECT status,filing_status FROM wallet_certificate WHERE wallet_id=?",
|
||||||
|
(rs, row) -> new CertificateRow(rs.getString("status"), rs.getString("filing_status")), payee.walletId);
|
||||||
|
if (certificates.isEmpty()) {
|
||||||
|
InstitutionSm2KeyPair keyPair = cryptography.generateSm2KeyPair();
|
||||||
|
try {
|
||||||
|
jdbc.update("INSERT INTO wallet_certificate (certificate_serial,wallet_id,public_key,encrypted_private_key," +
|
||||||
|
"filing_status,central_bank_root_signature,status,issued_at) " +
|
||||||
|
"VALUES (?,?,?,?, 'REGISTERED','TEACHING_PAYEE_ROOT','VALID',CURRENT_TIMESTAMP)",
|
||||||
|
"CERT_" + payee.walletId, payee.walletId, keyPair.getPublicKey(),
|
||||||
|
walletCipher.encrypt(keyPair.getPrivateKey()));
|
||||||
|
} catch (DuplicateKeyException exception) {
|
||||||
|
ensureCertificate(payee);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CertificateRow certificate = certificates.get(0);
|
||||||
|
if (!"VALID".equals(certificate.status) || !"REGISTERED".equals(certificate.filingStatus)) {
|
||||||
|
throw conflict("教学收款方证书状态异常");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureBankAccount(PayeeDefinition payee) {
|
||||||
|
List<AccountRow> accounts = jdbc.query("SELECT bank_code,status FROM simulated_bank_account WHERE account_id=?",
|
||||||
|
(rs, row) -> new AccountRow(rs.getString("bank_code"), rs.getString("status")), payee.accountId());
|
||||||
|
if (accounts.isEmpty()) {
|
||||||
|
try {
|
||||||
|
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) " +
|
||||||
|
"VALUES (?,?,?,?, '6222020000000000','0000',0,0,'ACTIVE',CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)",
|
||||||
|
payee.accountId(), payee.userId, payee.bankCode, payee.bankName);
|
||||||
|
} catch (DuplicateKeyException exception) {
|
||||||
|
ensureBankAccount(payee);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AccountRow account = accounts.get(0);
|
||||||
|
if (!payee.bankCode.equals(account.bankCode) || !"ACTIVE".equals(account.status)) {
|
||||||
|
throw conflict("教学收款方银行账户状态异常");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureBinding(PayeeDefinition payee) {
|
||||||
|
Integer count = jdbc.queryForObject("SELECT COUNT(*) FROM wallet_bank_binding " +
|
||||||
|
"WHERE wallet_id=? AND bank_account_id=? AND status='BOUND'", Integer.class,
|
||||||
|
payee.walletId, payee.accountId());
|
||||||
|
if (count != null && count > 0) return;
|
||||||
|
try {
|
||||||
|
jdbc.update("INSERT INTO wallet_bank_binding (wallet_id,bank_account_id,status,bound_at) " +
|
||||||
|
"VALUES (?,?,'BOUND',CURRENT_TIMESTAMP)", payee.walletId, payee.accountId());
|
||||||
|
} catch (DuplicateKeyException exception) {
|
||||||
|
Integer bound = jdbc.queryForObject("SELECT COUNT(*) FROM wallet_bank_binding " +
|
||||||
|
"WHERE wallet_id=? AND bank_account_id=? AND status='BOUND'", Integer.class,
|
||||||
|
payee.walletId, payee.accountId());
|
||||||
|
if (bound == null || bound == 0) throw exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BusinessException conflict(String message) {
|
||||||
|
return new BusinessException(ErrorCode.VALIDATION_ERROR, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class PayeeDefinition {
|
||||||
|
private final String userId;
|
||||||
|
private final String walletId;
|
||||||
|
private final String bankCode;
|
||||||
|
private final String bankName;
|
||||||
|
private final String organizationId;
|
||||||
|
|
||||||
|
private PayeeDefinition(String userId, String walletId, String bankCode, String bankName,
|
||||||
|
String organizationId) {
|
||||||
|
this.userId = userId;
|
||||||
|
this.walletId = walletId;
|
||||||
|
this.bankCode = bankCode;
|
||||||
|
this.bankName = bankName;
|
||||||
|
this.organizationId = organizationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String accountId() {
|
||||||
|
return "ACCOUNT_" + walletId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class WalletRow {
|
||||||
|
private final String userId;
|
||||||
|
private final String walletType;
|
||||||
|
private final String status;
|
||||||
|
|
||||||
|
private WalletRow(String userId, String walletType, String status) {
|
||||||
|
this.userId = userId;
|
||||||
|
this.walletType = walletType;
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class CertificateRow {
|
||||||
|
private final String status;
|
||||||
|
private final String filingStatus;
|
||||||
|
|
||||||
|
private CertificateRow(String status, String filingStatus) {
|
||||||
|
this.status = status;
|
||||||
|
this.filingStatus = filingStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class AccountRow {
|
||||||
|
private final String bankCode;
|
||||||
|
private final String status;
|
||||||
|
|
||||||
|
private AccountRow(String bankCode, String status) {
|
||||||
|
this.bankCode = bankCode;
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue