docs: API architecture ADRs, tech spec, and marketing capability tracker
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.
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user