17 KiB
设计文档
概述
珠宝商城微信小程序基于 uni-app (Vue 3) 开发,采用前后端分离架构。前端为微信小程序(用户端)和 Web 管理后台(管理员端),后端提供统一的 RESTful API 服务。系统核心围绕商品展示、购物车、订单管理、钻戒计算器和版房展示五大模块构建,不接入线上支付和物流追踪 API。
架构
整体架构
graph TB
subgraph 用户端["用户端 (微信小程序)"]
MP[uni-app Vue 3 小程序]
end
subgraph 管理端["管理端 (Web)"]
ADMIN[管理后台 Web 应用]
end
subgraph 后端服务["后端 API 服务"]
API[RESTful API]
AUTH[鉴权模块]
end
subgraph 数据层["数据层"]
DB[(MySQL 数据库)]
OSS[文件存储/OSS]
end
MP -->|HTTP 请求| API
ADMIN -->|HTTP 请求| API
API --> AUTH
API --> DB
API --> OSS
前端页面结构
graph LR
subgraph TabBar["底部 TabBar"]
HOME[首页/商品列表]
CART[购物车]
MOLD[版房专区]
MINE[我的]
end
HOME --> PRODUCT_DETAIL[商品详情]
HOME --> CATEGORY[分类筛选]
PRODUCT_DETAIL --> SPEC_PANEL[详细参数面板]
CART --> ORDER_SUBMIT[订单提交]
ORDER_SUBMIT --> ORDER_DETAIL[订单详情]
MINE --> ORDER_LIST[订单列表]
ORDER_LIST --> ORDER_DETAIL
MINE --> ADDRESS[收货地址管理]
MINE --> ABOUT[关于我们]
CALC[钻戒计算器] -.->|独立入口| TabBar
技术选型
| 层级 | 技术方案 |
|---|---|
| 前端框架 | uni-app + Vue 3 + Composition API |
| 状态管理 | Pinia |
| 网络请求 | uni.request 封装 |
| UI 组件 | uni-ui + 自定义组件 |
| 管理后台 | Vue 3 + Element Plus(Web 端) |
| 后端 API | Node.js + Express / Koa |
| 数据库 | MySQL |
| 文件存储 | 本地存储 / 云存储 OSS |
组件与接口
前端核心组件
1. 页面组件
| 组件 | 路径 | 职责 |
|---|---|---|
| HomePage | pages/home/index.vue |
商品列表、分类筛选 |
| ProductDetail | pages/product/detail.vue |
商品详情、banner、规格选择 |
| CartPage | pages/cart/index.vue |
购物车管理 |
| OrderSubmit | pages/order/submit.vue |
订单提交 |
| OrderDetail | pages/order/detail.vue |
订单详情 |
| OrderList | pages/order/list.vue |
订单列表 |
| MoldGallery | pages/mold/index.vue |
版房专区 |
| RingCalculator | pages/calculator/index.vue |
钻戒计算器 |
| MinePage | pages/mine/index.vue |
我的页面 |
| AddressManage | pages/address/index.vue |
收货地址管理 |
2. 公共组件
| 组件 | 职责 |
|---|---|
| ProductCard | 商品卡片(列表项) |
| SpecPanel | 详细参数选择弹窗 |
| BannerSwiper | 商品详情顶部轮播(图片+视频) |
| CustomerServiceBtn | 客服按钮(二维码/小程序客服) |
| ShippingNotice | 统一发货公告 |
API 接口设计
商品模块
GET /api/products # 商品列表(支持分类筛选、分页)
GET /api/products/:id # 商品详情(含 Base_Attribute)
GET /api/products/:id/specs # 商品详细参数选项
POST /api/products/:id/spec-data # 根据参数组合获取 Spec_Data_List
GET /api/categories # 商品分类列表
购物车模块
GET /api/cart # 获取购物车列表
POST /api/cart # 添加商品到购物车
PUT /api/cart/:id # 更新购物车项(数量等)
DELETE /api/cart/:id # 删除购物车项
订单模块
POST /api/orders # 提交订单
GET /api/orders # 获取用户订单列表
GET /api/orders/:id # 获取订单详情
PUT /api/orders/:id/cancel # 取消订单
版房模块
GET /api/molds # 版房列表(支持搜索)
GET /api/molds/:id # 版房详情
用户模块
POST /api/auth/wx-login # 微信登录
GET /api/user/profile # 用户信息
GET /api/user/addresses # 收货地址列表
POST /api/user/addresses # 新增收货地址
PUT /api/user/addresses/:id # 编辑收货地址
DELETE /api/user/addresses/:id # 删除收货地址
管理后台接口
# 商品管理
POST /api/admin/products # 创建商品
PUT /api/admin/products/:id # 编辑商品
DELETE /api/admin/products/:id # 删除商品
POST /api/admin/products/import # 规格数据表格导入
GET /api/admin/products/export # 规格数据表格导出
GET /api/admin/stock-alerts # 库存预警列表
# 订单管理
POST /api/admin/orders # 手动创建订单
PUT /api/admin/orders/:id # 修改订单
PUT /api/admin/orders/:id/status # 更新订单状态(已支付/已发货)
# 版房管理
POST /api/admin/molds # 创建版房信息
PUT /api/admin/molds/:id # 编辑版房信息
DELETE /api/admin/molds/:id # 删除版房信息
# 文件上传
POST /api/admin/upload # 上传图片/视频/支付凭证
数据模型
商品 (Product)
interface Product {
id: number
name: string // 商品名称
basePrice: number // 基础价格
styleNo: string // 款号
stock: number // 库存数量
totalStock: number // 总库存(用于计算库存百分比)
loss: number // 损耗
laborCost: number // 工费
categoryId: number // 分类 ID
bannerImages: string[] // banner 图片 URL 列表
bannerVideo?: string // banner 视频 URL
status: 'on' | 'off' // 上架/下架
createdAt: string
updatedAt: string
}
商品分类 (Category)
interface Category {
id: number
name: string
parentId?: number
sort: number
}
详细参数配置 (DetailParameterConfig)
interface DetailParameterConfig {
id: number
productId: number
fineness: string[] // 成色选项列表
mainStone: string[] // 主石选项列表
ringSize: string[] // 手寸选项列表
}
规格数据 (SpecData)
interface SpecData {
id: number
productId: number
modelName: string // 商品型号名称
fineness: string // 成色
mainStone: string // 主石
ringSize: string // 手寸
goldTotalWeight: number // 金料总重
goldNetWeight: number // 金料净重
loss: number // 损耗
goldLoss: number // 金耗
goldPrice: number // 金价
goldValue: number // 金值
mainStoneCount: number // 主石数量
mainStoneWeight: number // 主石石重
mainStoneUnitPrice: number // 主石单价
mainStoneAmount: number // 主石金额
sideStoneCount: number // 副石数量
sideStoneWeight: number // 副石石重
sideStoneUnitPrice: number // 副石单价
sideStoneAmount: number // 副石金额
accessoryAmount: number // 配件金额
processingFee: number // 加工工费
settingFee: number // 镶石工费
totalLaborCost: number // 总工费
totalPrice: number // 总价
}
购物车项 (CartItem)
interface CartItem {
id: number
userId: number
productId: number
specDataId: number // 关联的规格数据
quantity: number
product: Product // 关联商品信息
specData: SpecData // 关联规格数据
checked: boolean // 前端勾选状态(本地)
}
订单 (Order)
interface Order {
id: number
orderNo: string // 订单号
userId: number
status: 'pending' | 'paid' | 'shipped' | 'cancelled'
totalPrice: number
// 收货信息
receiverName: string
receiverPhone: string
receiverAddress: string
// 支付信息(已支付后填充)
paymentTime?: string
paymentProof?: string // 支付凭证图片 URL
// 物流信息(已发货后填充)
shippingCompany?: string // 物流公司名
shippingNo?: string // 物流单号
createdAt: string
updatedAt: string
}
订单商品项 (OrderItem)
interface OrderItem {
id: number
orderId: number
productId: number
specDataId: number
quantity: number
unitPrice: number
product: Product
specData: SpecData
}
版房信息 (MoldInfo)
interface MoldInfo {
id: number
name: string // 名称
styleNo?: string // 款号
barcodeNo?: string // 条码号
style?: string // 款式
images: string[] // 展示图片 URL 列表
createdAt: string
updatedAt: string
}
收货地址 (Address)
interface Address {
id: number
userId: number
name: string
phone: string
province: string
city: string
district: string
detail: string
isDefault: boolean
}
用户 (User)
interface User {
id: number
openId: string // 微信 openId
nickname: string
avatar: string
createdAt: string
}
钻戒计算器输入/输出 (RingCalculatorData)
interface RingCalculatorInput {
goldWeight: number // 金重(g)
mainStoneWeight: number // 主石重(ct)
sideStoneWeight: number // 副石重(ct)
lossRate: number // 损耗(倍率)
moldGoldPrice: number // 倒模金价(元)
mainStoneUnitPrice: number // 主石单价(元)
sideStoneUnitPrice: number // 副石单价(元)
sideStoneCount: number // 副石粒数(p)
microSettingFee: number // 微镶费(元/粒)
mainStoneSettingFee: number // 主石镶费(元)
threeDFee: number // 3D起板费(元)
basicLaborCost: number // 基本工费(元)
otherCost: number // 其他费用(元)
}
interface RingCalculatorResult {
netGoldWeight: number // 净金重(g)
weightWithLoss: number // 含耗重(g)
goldValue: number // 金值(元)
mainStoneTotal: number // 主石总价(元)
sideStoneTotal: number // 副石总价(元)
microSettingTotal: number // 微镶总价(元)
totalPrice: number // 总价(元)
}
钻戒计算器核心计算逻辑(纯函数)
function calculateRing(input: RingCalculatorInput): RingCalculatorResult {
const netGoldWeight = input.goldWeight - input.mainStoneWeight * 0.2 - input.sideStoneWeight * 0.2
const weightWithLoss = netGoldWeight * input.lossRate
const goldValue = weightWithLoss * input.moldGoldPrice
const mainStoneTotal = input.mainStoneWeight * input.mainStoneUnitPrice
const sideStoneTotal = input.sideStoneWeight * input.sideStoneUnitPrice
const microSettingTotal = input.sideStoneCount * input.microSettingFee
const totalPrice = goldValue + mainStoneTotal + sideStoneTotal
+ input.mainStoneSettingFee + microSettingTotal
+ input.threeDFee + input.basicLaborCost + input.otherCost
return {
netGoldWeight,
weightWithLoss,
goldValue,
mainStoneTotal,
sideStoneTotal,
microSettingTotal,
totalPrice,
}
}
正确性属性
属性(Property)是指在系统所有有效执行中都应成立的特征或行为——本质上是对系统应做什么的形式化陈述。属性是人类可读规格与机器可验证正确性保证之间的桥梁。
Property 1:购物车添加商品不变量
对于任意 商品和有效规格组合,将其添加到购物车后,购物车中应包含该商品和规格的条目,且购物车项数增加 1。
Validates: Requirements 2.1
Property 2:购物车总金额计算正确性
对于任意 购物车商品集合和勾选状态组合,展示的总金额应等于所有已勾选商品的单价乘以数量之和。
Validates: Requirements 2.3
Property 3:订单状态与展示字段匹配
对于任意 订单及其状态(待支付/已支付/已发货),订单详情展示的信息字段应与状态匹配:已支付状态应包含支付时间,已发货状态应包含物流公司名称和物流单号。
Validates: Requirements 3.5, 3.6
Property 4:钻戒计算器公式正确性
对于任意 有效的 RingCalculatorInput(所有数值字段为非负数),calculateRing 函数的输出应满足:
- netGoldWeight = goldWeight - mainStoneWeight × 0.2 - sideStoneWeight × 0.2
- weightWithLoss = netGoldWeight × lossRate
- goldValue = weightWithLoss × moldGoldPrice
- mainStoneTotal = mainStoneWeight × mainStoneUnitPrice
- sideStoneTotal = sideStoneWeight × sideStoneUnitPrice
- microSettingTotal = sideStoneCount × microSettingFee
- totalPrice = goldValue + mainStoneTotal + sideStoneTotal + mainStoneSettingFee + microSettingTotal + threeDFee + basicLaborCost + otherCost
Validates: Requirements 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8
Property 5:版房搜索结果匹配
对于任意 关键词和版房数据集,搜索返回的每一条结果的名称、款号、条码号或款式中至少有一个字段包含该关键词。
Validates: Requirements 5.3
Property 6:规格数据导入导出 Round-Trip
对于任意 有效的商品规格数据集合,导出为表格文件后再导入,应得到与原始数据等价的规格数据集合。
Validates: Requirements 8.3
Property 7:库存预警阈值判断
对于任意 商品集合,库存预警列表应恰好包含所有 stock/totalStock < 0.1 的商品,且按库存数量从少到多排序。
Validates: Requirements 8.4
Property 8:订单价格自动重算
对于任意 订单及其商品项变更(修改商品或型号),变更后的订单总价应等于所有订单项的单价乘以数量之和。
Validates: Requirements 9.4
错误处理
前端错误处理
| 场景 | 处理方式 |
|---|---|
| 网络请求失败 | 展示友好提示(如"网络异常,请稍后重试"),提供重试按钮 |
| 商品已下架/售罄 | 在商品详情页和购物车中标记状态,禁止加入购物车或下单 |
| 购物车为空时点击下单 | 提示"请先选择商品" |
| 订单提交失败 | 提示错误原因,保留用户填写的信息,支持重新提交 |
| 钻戒计算器输入非法值 | 输入框校验,提示"请输入有效数字",不执行计算 |
| 版房搜索无结果 | 展示"未找到匹配结果"提示 |
| 微信登录失败 | 提示用户重新授权,引导至授权页面 |
| 图片/视频加载失败 | 展示占位图,不影响页面其他内容 |
后端错误处理
| 场景 | 处理方式 |
|---|---|
| 请求参数校验失败 | 返回 400 状态码,附带具体字段错误信息 |
| 资源不存在 | 返回 404 状态码 |
| 未授权访问 | 返回 401 状态码,前端引导重新登录 |
| 管理员权限不足 | 返回 403 状态码 |
| 库存不足 | 返回业务错误码,前端提示"库存不足" |
| 订单状态流转非法 | 返回业务错误码(如已取消的订单不能改为已支付) |
| 规格数据导入格式错误 | 返回具体行号和错误原因,支持部分导入 |
| 文件上传失败 | 返回错误信息,前端提示重新上传 |
| 数据库操作异常 | 记录错误日志,返回 500 状态码,前端展示通用错误提示 |
订单状态流转规则
stateDiagram-v2
[*] --> 待支付 : 提交订单
待支付 --> 已支付 : 后台确认支付
已支付 --> 已发货 : 后台确认发货
待支付 --> 已取消 : 用户取消
已取消 --> [*]
已发货 --> [*]
合法状态流转:
- 待支付 → 已支付(仅后台操作)
- 已支付 → 已发货(仅后台操作)
- 待支付 → 已取消(用户或后台操作)
- 其他状态流转均为非法操作
测试策略
测试框架选型
| 类型 | 工具 |
|---|---|
| 单元测试 | Vitest |
| 属性测试 | fast-check + Vitest |
| 组件测试 | @vue/test-utils + Vitest |
| API 测试 | Supertest + Vitest |
属性测试(Property-Based Testing)
使用 fast-check 库进行属性测试,每个属性测试至少运行 100 次迭代。
每个属性测试必须以注释标注对应的设计文档属性编号:
// Feature: jewelry-mall, Property 4: 钻戒计算器公式正确性
属性测试覆盖的正确性属性:
- Property 1:购物车添加商品不变量
- Property 2:购物车总金额计算正确性
- Property 3:订单状态与展示字段匹配
- Property 4:钻戒计算器公式正确性
- Property 5:版房搜索结果匹配
- Property 6:规格数据导入导出 Round-Trip
- Property 7:库存预警阈值判断
- Property 8:订单价格自动重算
每个正确性属性由一个独立的属性测试实现。
单元测试
单元测试覆盖具体示例、边界情况和错误条件:
- 钻戒计算器:输入为 0 的边界情况、负数输入校验
- 购物车:空购物车操作、重复添加同一商品
- 订单:非法状态流转、缺少必填字段
- 版房搜索:空关键词、特殊字符
- 库存预警:库存恰好为 10% 的边界值
- 规格数据导入:格式错误、缺少必填列
组件测试
使用 @vue/test-utils 测试关键 Vue 组件的渲染和交互逻辑:
- 商品详情页 banner 渲染
- 购物车勾选和金额计算
- 订单提交页贵重物品提醒勾选逻辑
- 钻戒计算器输入和结果展示
API 集成测试
使用 Supertest 测试后端 API 的请求/响应:
- 商品 CRUD 操作
- 订单创建和状态流转
- 购物车操作
- 版房搜索
- 规格数据导入导出