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:
@@ -2,7 +2,9 @@
|
||||
|
||||
## Purpose
|
||||
The buyer-facing storefront: shell, home page, discovery, shopping and transaction flows, and the buyer center.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: Localized storefront
|
||||
The mall SHALL render every UI string and all catalog/store/marketing mock content in en or zh from one switcher, defaulting to en. Switching locale SHALL update the desktop shell and current page without a full reload.
|
||||
|
||||
@@ -304,3 +306,96 @@ The buyer center SHALL expose a "pending review" entry counting completed order
|
||||
- **WHEN** the reviews domain is configured to fixed data
|
||||
- **THEN** the review area, pending-review entry, and submission flows behave deterministically through the same shared client methods
|
||||
|
||||
### Requirement: Buyer wallet surface
|
||||
The mall SHALL render a buyer-center wallet page driven by the selected API adapter through `@vmall/shared`: available and frozen balance with currency, paginated fund entries, a clearly labeled demo recharge form, and a withdrawal request form. Balances and entries SHALL reflect backend state after each action rather than local-only state. The wallet SHALL be a mall API domain with fixed-adapter fallback methods and `LIVE_PICKS` wiring following the established per-domain adapter pattern.
|
||||
|
||||
#### Scenario: wallet page loads live state
|
||||
- **WHEN** a signed-in buyer opens the wallet page
|
||||
- **THEN** balances and the first page of fund entries render from the shared wallet contract
|
||||
|
||||
#### Scenario: demo recharge updates balance
|
||||
- **WHEN** the buyer submits a demo recharge
|
||||
- **THEN** the visible available balance reflects the credit without a reload and the form is visibly marked simulated
|
||||
|
||||
#### Scenario: withdrawal freezes visibly
|
||||
- **WHEN** the buyer submits a withdrawal request
|
||||
- **THEN** the visible summary shows available decreased and frozen increased by the requested amount, and the request appears in the withdrawal list as pending
|
||||
|
||||
#### Scenario: fixed adapter remains functional
|
||||
- **WHEN** the wallet domain is configured to fixed data
|
||||
- **THEN** the wallet page behaves deterministically through the same shared client methods
|
||||
|
||||
### Requirement: Merchant onboarding multi-step form
|
||||
The mall SHALL expose a merchant onboarding page ("商家入驻") reachable from the top-bar entry, with a multi-step form covering entity kind (personal 个人 / enterprise 企业), kind-specific entity information, operating categories from the published category tree, contact details, and qualification materials as URL input fields with no file-upload controls. The form SHALL be fillable while signed out, but submission SHALL require registration or sign-in and return the applicant to the completed form to submit through the shared selected API adapter. A duplicate-application conflict SHALL be surfaced inline. All copy SHALL come from the mall locale source in en and zh.
|
||||
|
||||
#### Scenario: anonymous fill then sign-in
|
||||
- **WHEN** a signed-out visitor completes the form and submits
|
||||
- **THEN** they are sent to register or sign in and, once signed in, returned to the completed form to submit
|
||||
|
||||
#### Scenario: enterprise kind shows company fields
|
||||
- **WHEN** the applicant selects the enterprise entity kind
|
||||
- **THEN** the company-specific entity and qualification fields replace the personal ones
|
||||
|
||||
#### Scenario: duplicate application surfaced
|
||||
- **WHEN** a signed-in user holding a pending or approved application submits the form
|
||||
- **THEN** the mall shows the conflict instead of silently creating a second application
|
||||
|
||||
#### Scenario: fixed adapter remains functional
|
||||
- **WHEN** the merchant-onboarding domain is configured to fixed data
|
||||
- **THEN** the form and status flows behave deterministically through the same shared client methods
|
||||
|
||||
### Requirement: Application status page
|
||||
The mall SHALL show a signed-in applicant their latest merchant application state — status, submitted entity kind, timestamps, and the rejection reason when rejected — linked from the onboarding page, with a re-apply action after rejection. Anonymous visitors SHALL be sent to sign in first.
|
||||
|
||||
#### Scenario: applicant tracks review
|
||||
- **WHEN** a signed-in applicant opens the status page while the application is `pending`
|
||||
- **THEN** the pending state and submission summary are shown
|
||||
|
||||
#### Scenario: rejection explains reason
|
||||
- **WHEN** a rejected applicant opens the status page
|
||||
- **THEN** the rejection reason is displayed with an action to apply again
|
||||
|
||||
### Requirement: Member center level page
|
||||
The buyer center SHALL render a membership level page from the selected API adapter: the current level's name, icon, and benefits, the total growth value, progress toward the next level's threshold, and the customer's growth history from the growth ledger. The page SHALL NOT derive level state from fixtures or local state, and it SHALL behave deterministically when the `membership` domain is configured to fixed data.
|
||||
|
||||
#### Scenario: level page reflects backend state
|
||||
- **WHEN** a signed-in shopper whose growth qualifies for a level opens the member-center level page
|
||||
- **THEN** the current level, benefits, growth total, and remaining growth to the next level render from the adapter without per-entry requests
|
||||
|
||||
#### Scenario: growth history lists ledger entries
|
||||
- **WHEN** a shopper opens the level page
|
||||
- **THEN** recent growth ledger entries with delta, reason, and time render paginated from the adapter
|
||||
|
||||
#### Scenario: fixed adapter remains functional
|
||||
- **WHEN** the `membership` domain is configured to fixed data
|
||||
- **THEN** the level page renders deterministic fixed level and growth data through the same shared client methods
|
||||
|
||||
### Requirement: Message center
|
||||
The mall SHALL render a user message center from the selected API adapter with a paginated message list and an unread-only filter. Opening or explicitly marking a message read, marking all read, and deleting a message SHALL persist through the API and update the visible list and counts without local-only mutation. Each message SHALL render its title and body in the active locale from the shared bilingual contract. Listing and refresh SHALL happen on entry and on demand; no push transport is required.
|
||||
|
||||
#### Scenario: unread filter and marking
|
||||
- **WHEN** a shopper filters the message center to unread and marks one message read
|
||||
- **THEN** the message persists as read and leaves the unread-only view with the unread count reduced
|
||||
|
||||
#### Scenario: mark all read
|
||||
- **WHEN** a shopper uses mark-all-read in the message center
|
||||
- **THEN** every unread message becomes read and the unread-only view empties
|
||||
|
||||
#### Scenario: delete a message
|
||||
- **WHEN** a shopper deletes a message
|
||||
- **THEN** it disappears from the list and the unread count through the API state
|
||||
|
||||
#### Scenario: fixed adapter remains functional
|
||||
- **WHEN** the `messaging` domain is configured to fixed data
|
||||
- **THEN** message listing, read marking, mark-all-read, and deletion behave deterministically through the same shared client methods
|
||||
|
||||
### Requirement: Top-bar unread badge
|
||||
The mall shell SHALL show the authenticated customer's unread message count as a badge on its message entry, read from the shared unread-count contract. The badge SHALL refresh on page entry and after read, mark-all-read, and delete actions, and SHALL be absent for anonymous shoppers.
|
||||
|
||||
#### Scenario: badge reflects unread count
|
||||
- **WHEN** a signed-in shopper with three unread messages loads any mall page
|
||||
- **THEN** the message entry badge shows three
|
||||
|
||||
#### Scenario: badge clears after mark-all-read
|
||||
- **WHEN** a shopper marks all messages read and returns to the shell
|
||||
- **THEN** the badge shows no unread count
|
||||
|
||||
Reference in New Issue
Block a user