The merge creates the brand capability (two requirements) and extends the catalog browse requirement with the brand filter, the sales sort and a real sold count. openspec validate --all --strict stays green at 14 specs. With every box now checked, docs/TBD-migrate-wave.md is deleted, as its own instructions require. Its two durable notes move to the README instead of disappearing with it: - `nuxt build` does not typecheck, so a green build is not the type gate the verification section claimed. AGENTS.md told me to treat it as one, which is why waves 1-5 over-reported "builds pass" as evidence. - the list of what stays deliberately mock: addresses, coupons, favourites, account stats, the seckill/collective/integral pages, reviews, and the fixed-data adapter itself, which the adapter spec requires for rollback. The README's verification block also gains the real test count (29, not 16).
4.1 KiB
4.1 KiB
Tasks
1. Schema and seed
- 1.1 Add a migration creating
brands(bilingual name, slug, position, active) andproducts.brand_idnullable withON DELETE SET NULL; verified the table and column exist aftercargo run -p vmall-api - 1.2 Seed the six demo brands and assign them to the demo products; verified a re-run is idempotent and all 24 products end up with a brand. The seed re-applies each product body on the 409 path so a re-run converges the assignment, and the brand list is captured in a local const — reading it back through the shared
rvariable worked once and then broke, because the product loop reassigns it
2. Shared contract
- 2.1 Add
BrandandBrandInput, the optionalbrand_idon the product payload andProductUpsertBody,brand_idonProductListQuery,"sales"on itssort, andsold_countonProduct; verified all three frontends build - 2.2 Add
listBrands(),admin.getBrands()/admin.replaceBrands(list)and fixed-data implementations, and register abrandsdomain in the per-domain switch. Also taught the fixed-datalistProductsto passbrand_id,sortandorderthrough — it had silently ignored all three, so the restored facet rendered but filtered nothing until the rollback check caught it
3. Catalog: brands
- 3.1 Add public
GET /api/brandsand adminPUT /api/admin/brands(transactional replace, slug and bilingual-name validation); verified a non-admin is refused and a duplicate slug is a 400 - 3.2 Add the
brand_idfilter tolistProducts, composing with the other filters, and returnbrand_idon the payload; verified a brand filter narrows 24 products to 6 and composes with the shop filter - 3.3 Accept
brand_idin the shop product upsert, on both create and update, so a merchant — and the seed — can set or clear it
4. Catalog: real sales
- 4.1 Compute
sold_countper product fromorder_itemsjoined to orders inpaid,fulfilling,shippedorcompleted, exposed on the list and detail payloads; verified a product with no paid orders reports zero - 4.2 Accept
sort=saleswithorder, keeping the 400 for any other value; verified the order matches the reported counts and that apending_paymentorder moves nothing - 4.3 Extend
apps/api/tests/catalog.rswithbrand_filter_and_real_sales, covering the brand filter, the unpaid exclusion, the paid count and the sales order; verifiedcargo test -p vmall-apiis green at 29 tests and repeatable
5. Mall surfaces
- 5.1
pages/search.vue: restored the brand facet fromlistBrands()and the sales sort; verified the facet renders only when brands exist and that brand plus category compose - 5.2
components/ui/ProductCard.vue: shows the product's realsold_countand no review figure; verified the card reads "N sold" only - 5.3
pages/goods/[id].vue: shows the real sold count, and the reviews tab, its summary and reply blocks are gone; verified the page offers only the detail and after-sale tabs, keeps the store card, and still shows the shop's after-sale copy - 5.4 Removed the now-unreferenced fabrication cluster from
apps/mall/mock/data.ts—salesOf,commentCountOf,commentsFor,commentStats,salesRankFor,productDetail,storeDetailand their types. Nothing imported them once the review UI went, and the fixed-data sales sort now orders bysold_count, which is zero there rather than an invented number
6. Verification
- 6.1 All three frontends build and
cargo test -p vmall-apiis green at 29 tests. The browser check remains the real gate, sincenuxt builddoes not typecheck - 6.2 Verified live in a browser: the search page shows the brand facet and a Sales sort, filtering by a brand narrows 24 products to 6, descending sales puts the sold products first with counts matching the API, product cards show only a real sold count, and the product page has no reviews while keeping its after-sale copy and store card
- 6.3 Verified the rollback: with every domain on fixed data and the backend stopped, the facet renders the fixed-data brands and filtering by one narrows 24 products to 2