Files
vmall/openspec/changes/replace-mock-api-wave-5/tasks.md
T
james 51f8bb7c1d docs(openspec): plan wave 5, the store directory
Planning only: proposal, delta specs, design and tasks for replacing the
store surfaces. No code yet.

The batch adds a public shop read (profile + products) so the store directory,
store home and product-page store card stop reading MOCK_STORES, and so the
cart, payment and order surfaces can name a shop instead of printing "Shop".

Decisions recorded in the design:

- a `shop_profiles` table beside `shops`, so the existing admin and
  shop-admin contracts are untouched
- the four store scores stay stored profile values, because there is no review
  model and deriving them would dress fabricated numbers as computed ones
- `distanceKm` is dropped rather than faked: there is no geo model behind it
- profiles are filled by seed-demo.mjs, since they hang off shops that script
  creates
- order surfaces resolve names from one cached shop read rather than growing a
  denormalised shop name on every order payload

openspec validate --strict passes and the change is ready to apply.
2026-09-17 16:54:22 +00:00

2.8 KiB

Tasks

1. Schema

  • 1.1 Add a migration creating shop_profiles keyed 1:1 to shops (shop_id primary key, logo, banner, company, region, address JSONB, notice JSONB, after_sale JSONB, four score columns, updated_at); verify the table exists after cargo run -p vmall-api

2. Shared contract

  • 2.1 Add ShopProfile (shop identity plus the optional profile fields) and a ShopProfileInput to packages/shared/src/types.ts; verify all three frontends build
  • 2.2 Add listShops() / getShop(slug) to the ApiClient and createApi, plus admin.setShopProfile(id, body); give the fixed-data adapter matching implementations built from MOCK_STORES so the rollback path still renders; verify the mall build

3. Public shop read

  • 3.1 Add GET /api/shops returning active shops with their profile, and GET /api/shops/{slug} returning one, both unauthenticated and both tolerating a missing profile row; verify a suspended shop is absent from the list and an unknown slug is a 404
  • 3.2 Add PUT /api/admin/shops/{id}/profile upserting the profile, validating non-empty en/zh on bilingual fields and gated to platform_admin; verify a bad payload is refused without changing the stored row

4. Demo profiles

  • 4.1 Teach scripts/seed-demo.mjs to set a profile for each demo shop using the existing /mock/store-N.svg assets and bilingual copy; verify a re-run is idempotent and GET /api/shops returns a profile for every shop

5. Mall store surfaces

  • 5.1 pages/stores/index.vue: list from GET /api/shops and drop the distance column and its sort; verify the directory renders every active shop and no "km" text remains
  • 5.2 pages/stores/[id].vue: load the shop by slug and its products from the catalog API by shop_id, drop the mock sales ranking in favour of the same shop-scoped product call; verify a store page renders its profile and products
  • 5.3 pages/goods/[id].vue: read the store card from the shop API instead of storeById, keeping the existing guards for a missing profile
  • 5.4 pages/checkout/pay.vue and pages/user/orders/index.vue: resolve shop names from the cached shop read, removing the generic "Shop" placeholder

6. Verification

  • 6.1 Run the mall, shop-admin and admin builds, since the shared contract changed; verify all three pass and cargo test -p vmall-api stays green
  • 6.2 With the backend seeded, verify in a browser that the store directory lists the demo shops, a store home renders its profile and products, the product page's store card shows a real name, and the cart/payment/order surfaces name the shop; confirm no console errors beyond deliberate failed responses
  • 6.3 Verify the rollback: with every domain on fixed data and the backend stopped, the store directory and store home still render