feat: three nuxt frontends, demo seed, rounding + money-exponent + rate-cast fixes, archived specs

This commit is contained in:
Chengdong Zhang
2026-09-17 13:34:38 +08:00
parent dc9fd31c5e
commit 653f13161a
85 changed files with 4327 additions and 106 deletions
@@ -0,0 +1,18 @@
# Proposal: catalog-i18n-currency
## Why
Merchants must manage products (create, edit, publish/unpublish) and shoppers must browse a localized, multi-currency storefront. This phase delivers the catalog core plus the currency infrastructure every price display depends on.
## What changes
- Schema: categories, products (JSONB i18n name/description), skus (price minor units + currency + stock), currencies (rate_to_base).
- Public APIs: product list (paged, filter by category/q/shop), product detail by id/slug, category list, currency list + conversion endpoint.
- Shop-admin APIs: product CRUD, SKU upsert, publish/unpublish transitions.
- Seed: base currency USD, plus CNY/EUR/JPY; demo categories.
## Non-goals
- Inventory reservations/warehouses, product variants matrix UI, image upload (URLs only).
- Full-text search engines; ILIKE search is sufficient for MVP.
## Capabilities
- `catalog`: categories, products, SKUs, publish lifecycle.
- `currency`: currency registry and conversion.
@@ -0,0 +1,37 @@
# Spec delta: catalog
## ADDED Requirements
### Requirement: Localized product content
Product and category names/descriptions SHALL be stored as JSONB maps keyed by locale (`en`, `zh`). The API MUST return the full map; clients pick the display locale.
#### Scenario: bilingual round-trip
- **WHEN** a shop owner creates a product with name `{"en": "Mug", "zh": "马克杯"}`
- **THEN** both public detail and shop-admin GET return the identical map
### Requirement: Publish lifecycle
Products SHALL have status `draft | published | unpublished`. Only `published` products appear in public list/detail.
#### Scenario: publish then unpublish
- **WHEN** a product is published
- **THEN** it appears in `GET /api/products`
- **WHEN** it is unpublished
- **THEN** public detail returns 404 and it disappears from listings
#### Scenario: publish requires sellable SKU
- **WHEN** publishing a product with no active SKU having price > 0
- **THEN** the API returns 400
### Requirement: Shop isolation
Shop-role users SHALL only see and mutate their own shop's products under `/api/shop/products`.
#### Scenario: cross-shop access denied
- **WHEN** shop owner A requests `/api/shop/products/{id}` of shop B
- **THEN** the API returns 404
### Requirement: SKU pricing
Each SKU SHALL carry `price_minor` (integer minor units) and an ISO `currency` code; stock is a non-negative integer.
#### Scenario: negative stock rejected
- **WHEN** upserting a SKU with stock < 0
- **THEN** the API returns 400
@@ -0,0 +1,21 @@
# Spec delta: currency
## ADDED Requirements
### Requirement: Currency registry
The system SHALL maintain a currencies table: ISO code, localized name, symbol, exponent (minor units), enabled flag, and `rate_to_base` (NUMERIC). Exactly one currency is the base.
#### Scenario: seeded currencies
- **WHEN** migrations finish
- **THEN** USD exists as base with rate 1, and CNY/EUR/JPY exist with positive rates
### Requirement: Amount conversion
`GET /api/currencies/convert` SHALL convert integer minor units between enabled currencies via base rates, rounding half-up to the target exponent.
#### Scenario: USD to JPY
- **WHEN** converting 1000 minor USD (=$10.00) to JPY with rate 150
- **THEN** the result is 1500 minor JPY (¥1500), an integer
#### Scenario: disabled currency rejected
- **WHEN** converting to a disabled or unknown currency
- **THEN** the API returns 400
@@ -0,0 +1,22 @@
# Tasks: catalog-i18n-currency
## 1. Schema
- [x] Migration: currencies, categories, products, skus; product status enum
- [x] Seed currencies (USD base, CNY, EUR, JPY) + demo categories (bilingual)
## 2. Currency APIs
- [x] GET /api/currencies (public, enabled only)
- [x] GET /api/currencies/convert?amount_minor&from&to
## 3. Public catalog APIs
- [x] GET /api/products (paged; filters: category_id, q, shop_id; only published)
- [x] GET /api/products/{id_or_slug} (published only, with SKUs)
- [x] GET /api/categories
## 4. Shop-admin catalog APIs
- [x] GET/POST /api/shop/products, PUT /api/shop/products/{id}
- [x] POST /api/shop/products/{id}/publish | /unpublish
- [x] POST /api/shop/products/{id}/skus (upsert by sku_code)
## 5. Tests
- [x] cargo test: publish lifecycle visibility, i18n fields round-trip, currency conversion math, shop isolation