Files
vmall/openspec/changes/archive/2026-09-25-add-membership-messaging/specs/messaging/spec.md
T
james 9904696e76 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.
2026-09-25 15:25:29 +00:00

50 lines
3.2 KiB
Markdown

## ADDED Requirements
### Requirement: System event messages
An order payment success, a shipment dispatch, and a refund completion (including after-sale refunds) SHALL each create one system message for the customer who owns the order. Each message SHALL record its kind, a bilingual title and body (`{en, zh}`) naming the affected order, and the order (and refund) reference it was triggered by. Emission SHALL be idempotent per customer, kind, and reference, so re-running an event handler never writes a duplicate message.
#### Scenario: paying an order notifies the customer
- **WHEN** a customer's order payment succeeds
- **THEN** an `order_paid` message referencing that order appears in the customer's inbox
#### Scenario: dispatch notifies the customer
- **WHEN** a shop marks a shipment of the customer's order as shipped
- **THEN** an `order_shipped` message referencing that order appears in the customer's inbox
#### Scenario: refund completion notifies the customer
- **WHEN** a refund for the customer's order completes
- **THEN** a `refund_completed` message referencing that order and refund appears in the customer's inbox
#### Scenario: re-run event handler does not duplicate
- **WHEN** the same event handler for the same order, kind, and reference runs twice
- **THEN** exactly one message exists for that customer, kind, and reference
### Requirement: User message state machine
A message SHALL belong to exactly one customer and move through `unread` and `read` states plus a soft deletion. Messages are created `unread`; marking one read or all read SHALL use a guarded update that touches only `unread` rows, so repeated marking never rewrites already-read rows. Deletion SHALL set a soft-delete marker rather than removing the row, SHALL be idempotent, and SHALL make the message invisible to listing and counting. Customers SHALL only ever read, mark, or delete their own messages.
#### Scenario: mark one message read
- **WHEN** a customer marks their unread message read twice
- **THEN** the message is read and only the first call changed its state
#### Scenario: mark all read touches only unread
- **WHEN** a customer with three unread and two read messages marks all read
- **THEN** exactly the three unread rows become read and the two read rows are untouched
#### Scenario: deleted messages disappear
- **WHEN** a customer deletes a message
- **THEN** it is absent from their list and unread count while its row remains for audit
#### Scenario: foreign messages are unreachable
- **WHEN** a customer addresses another customer's message for read or delete
- **THEN** the request fails without changing that message
### Requirement: Unread count endpoint
An authenticated customer SHALL read their unread message count through a dedicated endpoint that excludes soft-deleted messages. The count SHALL reflect read, mark-all-read, and delete actions immediately and SHALL only ever count the requesting customer's messages.
#### Scenario: count drops after mark-all-read
- **WHEN** a customer with four unread messages marks all read
- **THEN** the unread count endpoint returns zero
#### Scenario: deleting an unread message drops the count
- **WHEN** a customer deletes one of three unread messages
- **THEN** the unread count endpoint returns two