Files
vmall/openspec/changes/add-membership-messaging/tasks.md
T

5.9 KiB

1. Persistence and shared contract

  • 1.1 Add migration 0016_membership_messaging.sql: member_levels (bilingual name JSONB {en, zh}, icon, unique integer growth_threshold, bilingual benefits JSONB {en, zh}), growth_logs (append-only user_id, delta, running growth_total, reason, reference_type/reference_id order reference with a partial unique index per user and reference), messages (user_id, kind, bilingual title/body JSONB {en, zh}, reference_type/reference_id, status unread/read, deleted_at soft delete, partial unique index per user/kind/reference), a users.level column referencing member_levels, and listing/counting indexes.
  • 1.2 Add shared types (MemberLevel, MemberLevelInput, MembershipStatus, GrowthLogEntry, Message, MessageListQuery) and @vmall/shared methods getMembership, listGrowthLogs, listMessages, markMessageRead, markAllMessagesRead, deleteMessage, getUnreadCount, and admin.listMemberLevels, admin.createMemberLevel, admin.updateMemberLevel, admin.deleteMemberLevel with all amounts and growth values as integers and i18n content as {en, zh} JSONB.
  • 1.3 Implement apps/api/src/modules/membership/ repository, service, DTO, handlers, and module registration: admin-role-gated member-level CRUD routes and customer-scoped membership status and growth-ledger routes, services returning ApiResult<Dto>.
  • 1.4 Implement apps/api/src/modules/messaging/ repository, service, DTO, handlers, and module registration: customer-scoped message list (paginated, optional unread-only filter), mark-read, mark-all-read, soft delete, and unread-count routes returning ApiResult<Dto>.

2. Services and behavioral tests

  • 2.1 Implement growth accrual on order completion (customer confirms receipt): convert the order's realized paid amount to the base currency with integer minor-unit arithmetic (truncated whole units, no floating point), append exactly one ledger entry per order, and update users.level in the same transaction with a guarded UPDATE ... WHERE that only moves the customer to a strictly higher-threshold level.
  • 2.2 Wire idempotent message emission into the order, fulfillment, and refund (after-sale) transitions: order payment success emits order_paid, shipment dispatch emits order_shipped, and refund completion emits refund_completed, each a guarded insert keyed by user, kind, and reference with bilingual {en, zh} title and body naming the order.
  • 2.3 Add behavioral coverage in apps/api/tests/membership.rs and apps/api/tests/messaging.rs (fixtures from tests/common/mod.rs) for upgrade boundaries: growth exactly at a threshold upgrades, growth below every threshold holds no level, one accrual jumping two thresholds lands on the highest qualifying level, and repeated completion events accrue and upgrade exactly once.
  • 2.4 Extend the behavioral coverage for event triggers and read semantics: each of the three events creates exactly one correctly referenced message, re-run handlers do not duplicate, mark-read touches only unread rows (idempotent single and all-read), soft delete is idempotent and excluded from lists and unread counts, and cross-user message access fails.

3. Mall member and message surfaces

  • 3.1 Implement the getMembership, listGrowthLogs, listMessages, markMessageRead, markAllMessagesRead, deleteMessage, and getUnreadCount methods in apps/mall/mock/api.ts with deterministic per-session fixture state and the same idempotent read/delete semantics.
  • 3.2 Add the membership and messaging domains with their exact shared-client method picks to the Mall API selection and enable them in the default live runtime configuration.
  • 3.3 Add or adjust bilingual level, growth, message, badge, and failure strings through the existing Mall locale source without per-page hard-coded copy.
  • 3.4 Build apps/mall/pages/user/membership.vue: current level name/icon/benefits, growth total, progress to the next threshold, and paginated growth history from the shared contract, with fixed-adapter parity and no fixture imports.
  • 3.5 Build apps/mall/pages/user/messages.vue: paginated message list with unread-only filter, open/mark-read, mark-all-read, and delete actions that persist through the API and refresh list and counts.
  • 3.6 Add the top-bar unread badge on the shell message entry: unread count on page entry and after read/mark-all/delete actions, absent for anonymous shoppers, linking to the message center.

4. Admin member-level management

  • 4.1 Build apps/admin/pages/member-levels.vue: levels in threshold order with create/edit/delete forms over bilingual name, icon, growth threshold, and benefits through the shared admin level methods, surfacing the delete rejection for levels in use.
  • 4.2 Register the member-levels entry in the authenticated admin console navigation beside existing platform operations.

5. Verification

  • 5.1 Run the apps/api/tests/ integration suites with the tests/common/mod.rs fixtures — the new membership.rs and messaging.rs plus the affected orders.rs, order_service.rs, and points.rs suites — proving upgrade boundaries, event triggers, and read semantics against the shared test database.
  • 5.2 Browser-smoke the running API, Mall, and admin console: confirm receipt upgrades the level and appends the growth entry, the three system events land one message each, message-center read/mark-all/delete update lists and counts, the top-bar badge tracks unread count, and admin level CRUD with in-use delete rejection works end to end.
  • 5.3 Build the affected frontends because the shared API contract changes: pnpm --filter @vmall/mall build, pnpm --filter @vmall/admin build, and pnpm --filter @vmall/shop-admin build.
  • 5.4 Run openspec change validate add-membership-messaging --strict and openspec validate --all --strict and fix findings until both pass.