5.4 KiB
EDU-022 — Activate tenant-scoped native Pay transactions
- Status: done — bounded order/refund/notification takeover and native administration UI verified
- Type: platform reuse / financial isolation / database takeover
- Phase: 4 / commercialization foundation
- Blockers: EDU-020, EDU-021, native Pay runtime, PostgreSQL Flyway
Problem
EDU-020 made native Pay application/channel configuration operational, but the active PostgreSQL baseline still had no order, order-extension, refund, notification-task, or notification-log tables. The existing Pay controllers and Vben pages therefore could not administer real transaction state.
The stock transaction data objects were also inconsistent: notification tasks were tenant-aware, while orders, order extensions, refunds, and notification logs inherited only BaseDO. Silently creating global financial tables or assigning existing rows to a guessed tenant would make callbacks, exports, and background retries cross tenant boundaries.
Delivered contract
- V4340 creates Pay-owned
pay_order,pay_order_extension,pay_refund,pay_notify_task, andpay_notify_logtables for PostgreSQL. Education does not create a parallel order, refund, or webhook ledger. PayOrderDO,PayOrderExtensionDO,PayRefundDO,PayNotifyTaskDO, andPayNotifyLogDOall inheritTenantBaseDO, so normal MyBatis tenant interception scopes native admin queries and mutations.- Composite tenant foreign keys bind orders to Pay applications/channels, extensions to orders/channels, refunds to their application/channel/order, and logs to their notification task. Cross-tenant references fail in PostgreSQL even if application code is bypassed.
- Active merchant order/refund identifiers are unique inside a tenant application but may be reused by another tenant. Native Pay numbers and extension numbers are tenant-scoped.
- One active notification task is allowed for each
(tenant_id,type,data_id). This closes duplicate terminal-callback races; a deliberately soft-deleted task may be recreated. - Existing transaction tables without
tenant_idfail V4340. No global financial row is assigned to tenant0or inferred from an application ID. - The native order/refund callback entry points continue to resolve the channel first and execute the business update inside
TenantUtils.execute(channel.tenantId, ...). The existing notification retry job continues to use@TenantJob. - Existing Pay controllers remain authoritative:
/pay/orderand/pay/refundprovide tenant-filtered query/export contracts, while/pay/notifyprovides tenant-filtered task/detail reads and the provider callback entry points. - V4340 exposes the existing
pay/order/index,pay/refund/index, andpay/notify/indexVben pages below Education with the originalpay:order:*,pay:refund:*, andpay:notify:querypermissions. No custom Education transaction page was added.
Database safety
The circular order/extension relationship is created in two steps. The final fk_pay_order_extension installation is guarded through pg_constraint: an equivalent named composite tenant foreign key is accepted, a conflicting named constraint fails closed, and a missing constraint is installed. V4340 also verifies the complete required column shape after table creation.
The migration intentionally creates empty native transaction ledgers. It does not import legacy orders, payments, payment_events, or commerce_refund_requests; importing those records requires an explicit, reconciled mapping with amount/status/identifier/callback ownership rules.
Verification
- All 45 PostgreSQL Flyway integration tests pass through V4380. V4340 coverage proves same merchant order ID across tenants, composite-FK cross-tenant rejection, active notification-task uniqueness and soft-delete recreation, native menu/permission shape, successful migration history, and fail-closed adoption of a global
pay_ordertable. - The combined native Pay transaction regression passes 86 tests: 46 order, 28 refund, 11 notification, and one tenant-inheritance contract.
- The previously disabled
PayNotifyServiceTestis active; its asynchronous scheduling assertions and retry-count fixtures now match the production contract. mvn -pl yudao-server -am -DskipTests compileand the Vben@vben/web-antdtypecheck are the closing reactor/UI gates for this slice.
Explicitly open
- EDU-023 now supplies a bounded, terminal-only reconciled import. Production export tooling, reviewed Member-ID mapping, dry-run/runbook evidence, and operator sign-off remain required; direct table copying is still forbidden.
- EDU-024 now activates tenant-aware native Pay Transfer and Wallet persistence plus the existing administration pages. Historical opening balances remain deliberately unpopulated pending a reviewed source artifact.
- EDU-025 delivers native Mall Product activation and EDU-026 delivers native Promotion Coupon activation. Explicit legacy product/code-coupon import, Trade/other Promotion activation, and automatic purchase-to-entitlement fulfillment are not delivered.
- Refund completion does not yet revoke or shorten Education entitlements; commerce reconciliation must define partial-refund and replay semantics first.
- Coupons, commissions, referrals, dunning, settlement/reconciliation, generic Pay credential encryption/rotation, tenant PNVS, production deployment, and browser/API integration evidence remain separate work.