Persist the modular-monolith decision (ADR 0001 handler/service/repo, ADR 0002 keep REST) with the companion tech spec and the api-architecture OpenSpec capability. Replace the finished mock-migration tracker with docs/TBD-marketing.md listing the backend-less marketing domains still on fixtures (coupons, favorites, account stats, seckill, collective, integral, reviews). README and AGENTS.md point at the new docs.
83 lines
4.8 KiB
Markdown
83 lines
4.8 KiB
Markdown
# TBD — marketing capabilities with no backend (mall mock holdouts)
|
||
|
||
The mock→live migration finished at `replace-mock-api-wave-7` (address book, 2026-09-18).
|
||
Every domain that has an API is live; what follows is what still renders from
|
||
`apps/mall/mock/data.ts` because **no backend capability exists for it**. Each entry is a
|
||
new capability (schema + routes + contract + pages), not a domain flip — pick one up by
|
||
opening an OpenSpec change, the same way waves 1–7 did.
|
||
|
||
**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.
|
||
|
||
---
|
||
|
||
## Mock holdouts with a mall UI today
|
||
|
||
- [ ] **Coupons** — `user/coupons.vue` lists `MOCK_COUPONS`; `goods/[id].vue` shows a
|
||
claim strip off the same fixture.
|
||
Missing: `coupon_templates` (shop-issued: amount/threshold/window/stock), `coupons`
|
||
(user-held, order-bound status). APIs: `GET /api/coupons` (mine),
|
||
`POST /api/coupons/claim` (from a template), shop-admin template CRUD, and checkout
|
||
application (select → discount minor → bind to order). Money math stays minor units.
|
||
- [ ] **Favorites** — `user/favorites.vue` lists `MOCK_FAVORITES` (products tab +
|
||
stores tab); `user/index.vue` derives counts from it.
|
||
Missing: `favorites(user_id, product_id | shop_id)` with a partial unique index per
|
||
target kind. APIs: `GET/POST/DELETE /api/favorites` (product & shop variants).
|
||
- [ ] **Account stats** — `user/index.vue` shows `USER_STATS` (balance 128.00, points
|
||
2680, frozen 0) and `integral.vue` reuses the points figure.
|
||
Missing: balance/points accounts and ledgers (`money_logs` in the reference).
|
||
MVP shape: `GET /api/me/stats` returning `{ balance_minor, points, frozen_minor }`;
|
||
real ledgers only when a flow (recharge, refund-to-balance, points earn/spend) needs them.
|
||
|
||
## Marketing pages that are display-only mock
|
||
|
||
These exist as full pages (`seckill.vue`, `collective.vue`, `integral.vue`) linked from
|
||
the home navigation; all three read fixtures directly.
|
||
|
||
- [ ] **Seckill (秒杀)** — `SECKILL_SESSIONS` + `seckillProducts()` (price override,
|
||
sold %).
|
||
Missing: `seckill_sessions`, `seckill_products` (activity price, isolated stock),
|
||
`GET /api/seckill/sessions`, and checkout price resolution honouring the active session.
|
||
- [ ] **Collective / 拼团** — `collectiveProducts()` (need/joined counts).
|
||
Missing: `collective_activities`, `collective_groups` (open/join/expire, success on
|
||
fill); orders bind to a group; refund/rollback policy on expiry.
|
||
- [ ] **Integral mall / 积分商城** — `INTEGRAL_PRODUCTS` + points from `USER_STATS`.
|
||
Missing: points ledger (earn/spend), `integral_products`, points-denominated checkout
|
||
(`integral/orders` in the reference).
|
||
|
||
## Domains the reference has 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 / 提现**, **money logs** — qwshop user-center
|
||
modules; no mock, no UI, no model here.
|
||
- [ ] **Help center / articles** — nav links exist in the footer (`帮助中心`); no article
|
||
model. Cheap version: static content pages; full version: admin-managed articles.
|
||
- [ ] **OAuth login, SMS/captcha** — reference `users/oauth` + captcha plugin; here auth
|
||
is email+password only.
|
||
- [ ] **Freight templates / 运费模板** — shop-side shipping-fee rules; checkout currently
|
||
charges no shipping at all.
|
||
|
||
## 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.
|