# 设计文档 ## 概述 珠宝商城微信小程序基于 uni-app (Vue 3) 开发,采用前后端分离架构。前端为微信小程序(用户端)和 Web 管理后台(管理员端),后端提供统一的 RESTful API 服务。系统核心围绕商品展示、购物车、订单管理、钻戒计算器和版房展示五大模块构建,不接入线上支付和物流追踪 API。 ## 架构 ### 整体架构 ```mermaid 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 ``` ### 前端页面结构 ```mermaid 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) ```typescript 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) ```typescript interface Category { id: number name: string parentId?: number sort: number } ``` ### 详细参数配置 (DetailParameterConfig) ```typescript interface DetailParameterConfig { id: number productId: number fineness: string[] // 成色选项列表 mainStone: string[] // 主石选项列表 ringSize: string[] // 手寸选项列表 } ``` ### 规格数据 (SpecData) ```typescript 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) ```typescript interface CartItem { id: number userId: number productId: number specDataId: number // 关联的规格数据 quantity: number product: Product // 关联商品信息 specData: SpecData // 关联规格数据 checked: boolean // 前端勾选状态(本地) } ``` ### 订单 (Order) ```typescript 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) ```typescript interface OrderItem { id: number orderId: number productId: number specDataId: number quantity: number unitPrice: number product: Product specData: SpecData } ``` ### 版房信息 (MoldInfo) ```typescript interface MoldInfo { id: number name: string // 名称 styleNo?: string // 款号 barcodeNo?: string // 条码号 style?: string // 款式 images: string[] // 展示图片 URL 列表 createdAt: string updatedAt: string } ``` ### 收货地址 (Address) ```typescript interface Address { id: number userId: number name: string phone: string province: string city: string district: string detail: string isDefault: boolean } ``` ### 用户 (User) ```typescript interface User { id: number openId: string // 微信 openId nickname: string avatar: string createdAt: string } ``` ### 钻戒计算器输入/输出 (RingCalculatorData) ```typescript 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 // 总价(元) } ``` ### 钻戒计算器核心计算逻辑(纯函数) ```typescript 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 状态码,前端展示通用错误提示 | ### 订单状态流转规则 ```mermaid stateDiagram-v2 [*] --> 待支付 : 提交订单 待支付 --> 已支付 : 后台确认支付 已支付 --> 已发货 : 后台确认发货 待支付 --> 已取消 : 用户取消 已取消 --> [*] 已发货 --> [*] ``` 合法状态流转: - 待支付 → 已支付(仅后台操作) - 已支付 → 已发货(仅后台操作) - 待支付 → 已取消(用户或后台操作) - 其他状态流转均为非法操作 ## 测试策略 ### 测试框架选型 | 类型 | 工具 | |------|------| | 单元测试 | Vitest | | 属性测试 | fast-check + Vitest | | 组件测试 | @vue/test-utils + Vitest | | API 测试 | Supertest + Vitest | ### 属性测试(Property-Based Testing) 使用 fast-check 库进行属性测试,每个属性测试至少运行 100 次迭代。 每个属性测试必须以注释标注对应的设计文档属性编号: ```typescript // 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 操作 - 订单创建和状态流转 - 购物车操作 - 版房搜索 - 规格数据导入导出