feat: wave 2 migration (P3, P5, P7 openspec changes)
Implements, verifies, and archives the three remaining Wave 2 changes from openspec/MIGRATION-PLAN.md. - add-wallet-settlement (P3): demo recharge, guarded withdrawal freeze and one-time admin review, paginated own fund entries, idempotent per-shop weekly/monthly settlement statements with commission rate and one-time payout confirmation. - add-merchant-onboarding (P5): personal/enterprise applications with one live application per user, guarded review with mandatory rejection reason, and transactional shop + owner provisioning returning one-time credentials; mall onboarding/status pages and an admin review console. - add-membership-messaging (P7): platform member levels, append-only growth accrual on order completion with guarded one-way leveling, order/shipment/ refund system messages with unread/read state and soft deletion, plus the mall header unread badge. Backend: migrations 0019-0023, new wallet, settlement, merchant_onboarding, membership and messaging modules, event hooks in order/fulfillment/aftersale, and integration suites for each. Shared contract extended and all three frontends updated; code indexes, domain docs, backend guidelines and the migration tracker synced. Verification: cargo test -p vmall-api green twice consecutively; mall, admin and shop-admin builds pass; browser smoke on every new surface; openspec validate --all --strict green (33 passed). The three changes share the @vmall/shared contract, the mall mock adapter and per-app locale/nav files, so they are committed together to keep every commit buildable.
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
-- Wallet entry points over the existing customer-account ledger:
|
||||
-- simulated recharge records and withdrawal applications awaiting review.
|
||||
-- Balances themselves stay in customer_accounts; these tables are the
|
||||
-- business records whose lifecycle drives ledger entries.
|
||||
|
||||
CREATE TYPE wallet_recharge_status AS ENUM ('credited', 'failed');
|
||||
|
||||
CREATE TABLE wallet_recharges (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
||||
currency CHAR(3) NOT NULL REFERENCES currencies (code),
|
||||
amount_minor BIGINT NOT NULL CHECK (amount_minor > 0),
|
||||
status wallet_recharge_status NOT NULL DEFAULT 'credited',
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX wallet_recharges_user_idx
|
||||
ON wallet_recharges (user_id, created_at DESC);
|
||||
|
||||
CREATE TYPE wallet_withdrawal_status AS ENUM ('pending', 'approved', 'rejected');
|
||||
|
||||
CREATE TABLE wallet_withdrawals (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
||||
currency CHAR(3) NOT NULL REFERENCES currencies (code),
|
||||
amount_minor BIGINT NOT NULL CHECK (amount_minor > 0),
|
||||
-- Free-form payout destination captured at application time.
|
||||
account_details JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
status wallet_withdrawal_status NOT NULL DEFAULT 'pending',
|
||||
reviewed_by UUID REFERENCES users (id),
|
||||
reviewed_at TIMESTAMPTZ,
|
||||
review_note TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
-- A reviewed row always records who and when; a pending row never does.
|
||||
CONSTRAINT wallet_withdrawals_review_consistent CHECK (
|
||||
(status = 'pending' AND reviewed_by IS NULL AND reviewed_at IS NULL)
|
||||
OR (status <> 'pending' AND reviewed_by IS NOT NULL AND reviewed_at IS NOT NULL)
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX wallet_withdrawals_user_idx
|
||||
ON wallet_withdrawals (user_id, created_at DESC);
|
||||
CREATE INDEX wallet_withdrawals_status_idx
|
||||
ON wallet_withdrawals (status, created_at DESC);
|
||||
Reference in New Issue
Block a user