james e540b91491 feat(mall): live group-buying page with open or join intent
The group-buying page lists active activities, the group price against the
catalog price, and each open group with its paid-member count and expiry. A
shopper opens a group or picks one to join; either way the activity SKU goes
into the cart and the intent is carried to checkout, which submits it and hides
the coupon picker for that order. Payment and order detail show the snapshotted
group price.

The group-buying fixtures leave the page; the fixed-data adapter still serves
the domain as the rollback path.
2026-09-18 13:22:47 +00:00

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/sharedTS 类型、API client、en/zh 语言包、共享样式
openspec/       OpenSpec 规范(specs/ 为归档后的能力规范)
docs/adr/       Architecture Decision Records(英文)
docs/tech-specs/ 内部技术规格(英文)
scripts/
  seed-demo.mjs 演示数据脚本

技术要点:

  • 金额:整数最小单位(minor units)+ ISO 币种码存储,禁止浮点。rate_to_base 表示"1 基准币 = N 本币",换算四舍五入(half-up)。
  • 多语言:商品/店铺/分类/币种内容以 JSONB {"en","zh"} 存储;UI 文案在 @vmall/shared locales + 各应用 locales-extra.ts
  • RBACplatform_adminshop_ownershop_staff(带 shop_id 作用域)、customerJWT24h)。
  • 下单:Redis 购物车 → 单事务结算,按店铺拆单,价格快照 + 扣库存;缺货整单 409 回滚;取消恢复库存。
  • 发货单:支持部分发货(校验剩余量),状态联动 fulfilling → shipped → completed。
  • 发票:每单最多一张有效发票;企业发票必填税号;商户开具生成发票号。

快速开始

前置:Docker 中的 Postgres(容器 pg18postgres/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        # 29 个集成测试(vmall_test + Redis
pnpm --filter @vmall/mall build       # 打包;注意:并不做类型校验
pnpm --filter @vmall/shop-admin build
pnpm --filter @vmall/admin build
openspec validate --all --strict      # 规范校验

nuxt build 不做类型校验:nuxt.config.ts 未开启 typescript.typeCheck,也未安装 vue-tsc。构建通过只说明能打包。要补上这道关:加 vue-tsc + typescript 依赖、设 typescript: { typeCheck: true },首次运行会暴露一批既有错误。在那之前,能证明页面可用的是浏览器实测,而不是构建

商城的 mock 边界

apps/mall 通过 apps/mall/plugins/api.tsliveDomains 按域选择适配器。已有 API 的域全部走真实后端:catalog、currency、content、brands、shops、auth、cart、orders、shipments、invoices、addresses。

仍来自 ~/mock/data 的部分都是没有后端能力的营销/账户域,逐项记录在 docs/TBD-marketing.md(优惠券、收藏、账户统计、秒杀、拼团、积分商城、评价等)。每一项都是一次新的能力建设,不是适配层切换。

  • fixed-data 适配器本身:Mock API adapter 规范要求它仍能服务每个域,因此它是回滚路径,删除它会破坏回滚。

环境变量(后端)

变量 默认值
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
S
Description
No description provided
Readme
1.2 MiB
Languages
Rust 41.8%
Vue 36.3%
TypeScript 18.3%
JavaScript 2.7%
CSS 0.9%