e1a0a5dbdb58b079af61e3a251c02253852a27d3
Wave 3 of replacing the fixed-data mock adapter: cart, orders, shipments and invoices flip together, so one purchase runs end to end against the backend. - cart: CartItemView carries the line's shop and the SKU's stock, so the cart keeps grouping per shop and the quantity stepper caps at real stock instead of a hard-coded 999 - contract: Shipment.items is optional and Invoice.invoice_no nullable, both matching what the API actually returns. Invoice was declared twice in types.ts and TypeScript merges duplicate interfaces, so the duplicate had to go for the change to take effect at all - an anonymous add-to-cart redirects to /login?redirect=..., and sign-in honours only same-origin paths - the fixed-data adapter learns the new cart fields, and its persisted state key moves to v2 because a cart saved by an older build is no longer valid - order surfaces drop their storeById lookups and keep the generic store label until the public store read arrives Verified end to end: two-shop cart grouping with live shop names, stock caps read from the API, checkout, payment, shipment, delivery confirmation and an issued invoice. Rollback re-verified with every domain on fixed data and the backend stopped. Also checks off Wave 3 in docs/TBD-migrate-wave.md and re-points that file at the mock content that remains. OpenSpec change: openspec/changes/replace-mock-api-wave-3
VMall — B2B2C 商城 MVP
前后端分离的多商户(B2B2C)商城。一个 Rust 后端,三个 Nuxt 前端,Postgres + Redis(本地 Docker 提供),OpenSpec 驱动开发。
架构
apps/
api/ Rust 后端(axum 0.8 + sqlx + redis) http://localhost:8080/api
mall/ Nuxt 3 顾客商城 http://localhost:3000
shop-admin/ Nuxt 3 商户后台 http://localhost:3001
admin/ Nuxt 3 平台后台 http://localhost:3002
packages/
shared/ @vmall/shared:TS 类型、API client、en/zh 语言包、共享样式
openspec/ OpenSpec 规范(specs/ 为归档后的能力规范)
scripts/
seed-demo.mjs 演示数据脚本
技术要点:
- 金额:整数最小单位(minor units)+ ISO 币种码存储,禁止浮点。
rate_to_base表示"1 基准币 = N 本币",换算四舍五入(half-up)。 - 多语言:商品/店铺/分类/币种内容以 JSONB
{"en","zh"}存储;UI 文案在@vmall/sharedlocales + 各应用locales-extra.ts。 - RBAC:
platform_admin、shop_owner、shop_staff(带 shop_id 作用域)、customer;JWT(24h)。 - 下单:Redis 购物车 → 单事务结算,按店铺拆单,价格快照 + 扣库存;缺货整单 409 回滚;取消恢复库存。
- 发货单:支持部分发货(校验剩余量),状态联动 fulfilling → shipped → completed。
- 发票:每单最多一张有效发票;企业发票必填税号;商户开具生成发票号。
快速开始
前置:Docker 中的 Postgres(容器 pg18,postgres/postgres)与 Redis(容器 rdb8)已运行;Rust 1.98+、Node 22+、pnpm 10+。
# 1. 数据库(只需一次)
docker exec pg18 psql -U postgres -c "CREATE DATABASE vmall;" -c "CREATE DATABASE vmall_test;"
# 2. 安装依赖
pnpm install
# 3. 启动后端(自动执行 sqlx 迁移并播种平台管理员)
cargo run -p vmall-api
# 4. 播种演示数据(店铺、店主、4 个双语商品)
node scripts/seed-demo.mjs
# 5. 启动前端(另开终端)
pnpm dev:mall # :3000
pnpm dev:shop-admin # :3001
pnpm dev:admin # :3002
演示账号:
| 角色 | 账号 | 密码 | 应用 |
|---|---|---|---|
| 平台管理员 | admin@vmall.local | admin1234 | admin :3002 |
| 店主 | shop@vmall.local | shop12345 | shop-admin :3001 |
| 顾客 | customer@vmall.local | customer123 | mall :3000 |
测试与校验
cargo test -p vmall-api # 16 个集成测试(vmall_test + Redis)
pnpm --filter @vmall/mall build # 各前端构建即类型校验
pnpm --filter @vmall/shop-admin build
pnpm --filter @vmall/admin build
openspec validate --all --strict # 规范校验
环境变量(后端)
| 变量 | 默认值 |
|---|---|
DATABASE_URL |
postgres://postgres:postgres@127.0.0.1:5432/vmall |
REDIS_URL |
redis://127.0.0.1:6379/ |
JWT_SECRET |
vmall-dev-secret-change-me |
PORT |
8080 |
JWT_TTL_SECS |
86400 |
Languages
Rust
41.8%
Vue
36.3%
TypeScript
18.3%
JavaScript
2.7%
CSS
0.9%