5.1 KiB
EDU-024 — Activate native Pay Transfer and Wallet
- Status: done — tenant-owned native ledgers, security hardening, existing Pay APIs/UI, and PostgreSQL evidence delivered
- Type: native module takeover / financial ledger / tenant isolation
- Phase: 4 / commercialization foundation
- Blockers: EDU-020, EDU-022, PostgreSQL Flyway
Problem
RuoYi already provides Transfer and Wallet services, callbacks, jobs, permissions, and Vben pages, but their persistence was not activated by the Education PostgreSQL migration line and the corresponding data objects were not tenant-aware. Reimplementing those capabilities inside Education would create a second financial ledger. Inferring a wallet balance from the legacy backend would be unsafe because the legacy schema has no equivalent authoritative balance aggregate.
Delivered contract
- Existing native Pay contracts remain authoritative:
/pay/transfer,/pay/wallet,/pay/wallet-transaction,/pay/wallet-recharge, and/pay/wallet-recharge-package. No Education transfer, balance, recharge, or transaction controller was added. PayTransferDO,PayWalletDO,PayWalletTransactionDO,PayWalletRechargeDO, andPayWalletRechargePackageDOnow extendTenantBaseDO. Transfer synchronization keeps@TenantJob, and framework tenant injection scopes all normal mapper access.- Wallet locks now use
pay_wallet:lock:{tenantId}:{walletOrUserId}so the same native identifier in two tenants cannot serialize against or interfere with the other tenant's operation. - Amount-changing service methods reject null, zero, and negative amounts. Administrator balance reductions use the conditional subtract path and cannot create a negative balance or incorrectly increase the member's lifetime expense total.
Integer.MIN_VALUEcannot be negated through the admin request contract. - Wallet recharge refund completion uses the persisted recharge
walletId; it does not dereference a separately loaded wallet. The refund action has its ownpay:wallet-recharge:refundpermission. - Wallet-provider transfer status lookup uses the native transfer number, matching the business key used when the wallet transaction was created.
- Request DTOs validate positive user/package IDs, valid wallet business types, positive add amounts, non-zero admin adjustments, package name/amount/status shape, and non-negative bonus amounts.
- The existing Vben
pay/transfer/index,pay/wallet/balance/index, andpay/wallet/rechargePackage/indexpages and APIs are reused. Member administration continues to provide the permission-aware balance-adjustment action.
Database contract
V4360 creates empty tenant-scoped pay_transfer, pay_wallet, pay_wallet_transaction, pay_wallet_recharge, and pay_wallet_recharge_package ledgers. It never imports or invents a historical wallet balance.
Composite tenant foreign keys bind transfers to native Pay App/Channel and wallet rows to their Wallet, Package, Order, and Refund owners. Balances, frozen amounts, and cumulative totals cannot be negative. A tenant can have only one active wallet per (userId,userType), while the same identifiers remain valid in another tenant. Non-administrator transaction business keys are tenant-idempotent. Existing global tables fail closed instead of being silently adopted.
V4360 also seeds the existing Vben routes and granular Transfer query/export, Wallet query/update, Recharge Package CRUD, and Recharge refund permissions.
Verification
- Twelve focused Pay tests pass across transfer service behavior, tenant-aware DO/job/permission contracts, tenant-qualified Redis locking, safe positive/negative admin adjustments, non-positive amount rejection, recharge-refund wallet identity, and wallet-provider transfer lookup.
- All 45 PostgreSQL Flyway integration tests pass through V4380. V4360 coverage proves same identifiers across tenants, same-tenant wallet uniqueness, cross-tenant Wallet Transaction and Transfer foreign-key rejection, database rejection of negative balances, exact menu/permission shape, migration history, and fail-closed global-wallet adoption.
- The existing Vben Transfer/Wallet/Recharge Package APIs and pages pass formatting, lint, and
@vben/web-antdtypecheck as part of the combined UI verification.
Explicitly open
- Production opening balances require a separately reviewed, reconciled source artifact and operator runbook. This slice deliberately creates empty wallet ledgers because the legacy system has no equivalent balance authority.
- Transfer initiation, wallet recharge, channel callbacks, and refunds still require target-environment credentials, App/Channel setup, explicit role grants, and browser/API integration evidence.
- EDU-025 now activates native Mall Product persistence and existing administration without importing legacy display rows. Promotion/Trade, explicit legacy product import, automatic purchase fulfillment, refund-driven entitlement revocation, settlement reconciliation, commissions, referrals, dunning, generic credential encryption/rotation, tenant PNVS, legacy activation-code import, coupons, and production bulk financial migration remain separate slices.