Replace mall fixture favorites with customer-scoped endpoints, and send signed-out shoppers back to the page they left after sign-in. Co-authored-by: Cursor <cursoragent@cursor.com>
132 lines
8.4 KiB
Markdown
132 lines
8.4 KiB
Markdown
# TBD — marketing capabilities with no backend (mall mock holdouts)
|
|
|
|
The mock→live migration and the first marketing implementation wave are complete. Every domain
|
|
with an API is live; only the unchecked items below still render from `apps/mall/mock/data.ts`
|
|
because no backend capability exists for them. Each remaining entry is a new capability, not a
|
|
domain flip, and requires its own OpenSpec change.
|
|
|
|
**How to use:** check a box only once the behaviour is implemented *and* verified against
|
|
the live backend (`cargo run -p vmall-api`, `node scripts/seed-demo.mjs`), then remove the
|
|
mock data and the page's `~/mock/data` import in the same change.
|
|
|
|
**Delete this file** once every box is checked, or consciously dropped and recorded. The
|
|
"deliberately out of scope" list at the bottom does not block deleting it.
|
|
|
|
---
|
|
|
|
## Implementation report — 2026-09-21
|
|
|
|
Five independent OpenSpec changes were implemented, verified, archived, and synced to main specs:
|
|
|
|
| Change | Business boundary | Prerequisite |
|
|
| --- | --- | --- |
|
|
| `add-shop-coupons` | Shop templates, customer claims, per-shop checkout redemption, and cancellation restoration | None |
|
|
| `add-customer-accounts` | Monetary/points accounts, append-only ledger, and live account stats | None |
|
|
| `add-points-mall` | Platform points catalog, atomic redemption, customer history, and admin fulfillment | `add-customer-accounts` must archive first |
|
|
| `add-flash-sales` | Timed sessions, SKU activity inventory, limits, and checkout pricing | None |
|
|
| `add-group-buying` | Activities, group lifecycle, paid membership, and payment-time capacity | None |
|
|
|
|
Each change is archived under `openspec/changes/archive/2026-09-18-<change>/`; its requirements
|
|
are present in the main OpenSpec specs. Backend migrations `0010` through `0014`, API modules,
|
|
shared contracts, and live frontend domain picks are present. Source-project names are absent from
|
|
the planning artifacts.
|
|
|
|
**Captured decisions:** coupons are template plus customer-owned snapshot and one per shop order;
|
|
activity-priced shop orders (flash or group) reject coupons; a SKU cannot be in overlapping flash
|
|
and group windows; account debits use guarded atomic updates with immutable entries; points
|
|
redemption uses a separate order lifecycle and demo points are seed-credited through the ledger;
|
|
flash sales use activity-reserved stock and server-calculated prices; group seats are claimed at
|
|
payment rather than checkout; an unpaid opener cancel closes an empty group; catalog product-detail
|
|
does not show flash or group prices in these changes.
|
|
|
|
**Implemented order:** `add-customer-accounts`, `add-shop-coupons`, `add-points-mall`,
|
|
`add-flash-sales`, then `add-group-buying`. Favorites is implemented as `add-favorites`. The
|
|
deferred designs below intentionally have no change yet.
|
|
|
|
## Mock holdouts with a mall UI today
|
|
|
|
- [x] **Coupons** — implemented and archived as `2026-09-18-add-shop-coupons`; mall product,
|
|
customer coupon, and checkout surfaces use the live coupon API.
|
|
- [x] **Favorites** — implemented as `add-favorites`; customer product/shop favorites persist
|
|
through the live API. Mall product/store detail, buyer-center list, and dashboard preview
|
|
use the selected adapter instead of `MOCK_FAVORITES` or local heart state.
|
|
- [x] **Account stats** — implemented and archived as `2026-09-18-add-customer-accounts`.
|
|
The mall reads the live summary; append-only ledgers and guarded mutation back later flows.
|
|
Frozen balance remains zero until a freeze flow exists.
|
|
|
|
## Implemented marketing pages
|
|
|
|
The former display-only pages now use live backend domains; their fixtures remain only inside the
|
|
fixed-data adapter as the required rollback implementation.
|
|
|
|
- [x] **Seckill (秒杀)** — implemented and archived as `2026-09-18-add-flash-sales`.
|
|
Catalog product detail intentionally does not show activity prices.
|
|
- [x] **Collective / 拼团** — implemented and archived as `2026-09-18-add-group-buying`.
|
|
Paid seats are claimed at payment; unpaid opener cancellation closes an empty group.
|
|
- [x] **Integral mall / 积分商城** — implemented and archived as
|
|
`2026-09-18-add-points-mall`; demo points are ledger credits and earning campaigns remain out.
|
|
|
|
## Domains typical B2B2C storefronts have and this MVP does not (no UI here)
|
|
|
|
Recorded so the gap is explicit, not because all of them belong in scope:
|
|
|
|
- [ ] **Reviews / 评价** — no model; the mall presents none (review counts, the detail
|
|
page's review tab/summary/replies were removed in wave 6 rather than kept invented).
|
|
Missing: `order_comments` (order-item bound, rated, replyable), public read on product
|
|
pages, shop reply, admin moderation. Writing/moderating/displaying reviews is a feature
|
|
with its own lifecycle.
|
|
- [ ] **Distribution / 分销**, **cashes / 提现** — common B2B2C account modules; no mock,
|
|
no UI, no model here. Customer-account freeze/credit is the intended foundation.
|
|
Public money-log listing is not in `add-customer-accounts`. Deferred design notes below.
|
|
- [ ] **Help center / articles** — header nav `shell.nav.help` currently links to `/user`;
|
|
the footer has no help links. No article model. Cheap version: static locale pages;
|
|
full version: admin-managed articles.
|
|
- [ ] **OAuth login, SMS/captcha** — common storefront plugins; here auth is email+password only.
|
|
- [ ] **Freight templates / 运费模板** — shop-side shipping-fee rules; checkout currently
|
|
charges no shipping at all.
|
|
|
|
## Deferred designs from general B2B2C storefronts
|
|
|
|
These patterns are recorded for later product discovery only. They are not approved scope,
|
|
have no OpenSpec change, and must be redesigned under VMall's minor-unit, JSONB, RBAC, and
|
|
transaction rules before implementation.
|
|
|
|
- [ ] **Threshold reductions / 满减** — merchant rules need a shop scope, localized name,
|
|
threshold and reduction in one currency, active window, and an explicit best-eligible rule.
|
|
Coupon vs flash/group is already exclusive (reject coupon on activity-priced shop orders).
|
|
Keep 满减 independent from coupons until a stacking policy with those shop coupons is specified.
|
|
- [x] **Favorites** — implemented as `add-favorites` with explicit nullable product/shop
|
|
foreign keys, an exactly-one-target check, and one partial unique index per kind.
|
|
Do not revisit a polymorphic `target_id`/type pair.
|
|
- [ ] **Reviews / 评价** — a future review belongs to a fulfilled order item, not just a
|
|
product. Preserve an immutable rating/content snapshot, allow one shop reply, and make
|
|
public visibility and platform moderation explicit lifecycle states.
|
|
- [ ] **Distribution / 分销** — a future merchant activity may configure per-product levels,
|
|
but commissions must be created as order-item ledger entries and become payable only after
|
|
the order reaches its chosen settlement condition. Do not use floating commission rates or
|
|
mutate a balance without a ledger entry.
|
|
- [ ] **Withdrawals / 提现** — model a payout request with account, amount minor, fee minor,
|
|
destination snapshot, and `requested → approved | rejected | paid` transitions. Funds must
|
|
be frozen and released or debited atomically with the transition.
|
|
- [ ] **Promotion window exclusivity as a database invariant** — flash sales and group buying
|
|
currently keep one SKU out of overlapping activity windows with cross-table queries, and the
|
|
flash-sales guard stays inert until the group-buying table exists. A shared
|
|
`promotion_windows(sku_id, kind, starts_at, ends_at)` table with
|
|
`EXCLUDE USING gist (sku_id WITH =, tstzrange(starts_at, ends_at) WITH &&)` would make an
|
|
overlap impossible to insert and remove the creation-order problem. Deferred because it
|
|
retrofits the archived flash-sales schema; revisit if a third timed activity type appears.
|
|
|
|
## Deliberately out of scope — does not block deleting this file
|
|
|
|
- **The fixed-data adapter itself** (`apps/mall/mock/api.ts`, `~/mock/data`): the
|
|
Mock API adapter spec requires it to keep serving every domain as the rollback path.
|
|
Removing the fixtures above means pages stop *reading* them; the adapter stays.
|
|
|
|
## Invariants to keep when implementing any box
|
|
|
|
- Money is `i64` minor units + currency code; no float math anywhere.
|
|
- New user-facing content fields are `{en, zh}` JSONB; UI copy goes through `$t()`.
|
|
- Contract changes land only in `packages/shared` and all three frontends must still build.
|
|
- State transitions validate preconditions (`UPDATE ... WHERE status = ...` pattern).
|
|
- Each capability gets its own `openspec/changes/<name>/` and archives green.
|