基于Spring Boot4与Vue3的珠宝首饰小程序电商平台实现

📅 发布时间:2026/9/4 4:41:20
基于Spring Boot4与Vue3的珠宝首饰小程序电商平台实现
如果只把“珠宝首饰交易小程序”当成普通商城来做你很可能在答辩、演示或 Code Review 时被问住商品价格到底存在哪一层下单时库存怎么扣才不超卖订单里的成交价为什么不能让前端随便传用户在微信小程序里登录后后端怎么知道“他是谁”这些问题正是“基于 Spring Boot 4 Vue3 微信小程序的珠宝首饰电商平台”这类项目真正要回答的东西。我的判断很明确这套项目真正的难点不在“用了 Spring Boot 还是 Vue3”也不在“小程序页面能画得多精美”而在于能不能把“会员 - 商品 - 购物车 - 订单 - 支付/库存”这一条交易链路的状态保持一致。珠宝首饰又是典型的高客单价、强款式属性商品和普通日用品商城相比它对 SKU 设计、价格快照、库存精确性、权限安全的要求更高。把这个项目写清楚比单纯写一个“商品增删改查”有价值得多。这篇文章会以“SpringBoot4 Vue3 微信小程序”的珠宝首饰交易系统为蓝本从架构拆解讲到数据库设计再给出后端登录、下单事务、Vue3 管理后台、小程序商城端的可复用代码骨架。你不用一次性拿到全部源码也能照着这个思路自己搭出一个可演示、可扩展、经得起提问的电商闭环。文中大部分代码是工程骨架式的实现示例放到真实项目中时需要根据自己的包名、字段和权限方案做适配。1. 这篇文章真正要解决的问题1.1 “做一个电商系统”为什么不是 CRUD 那么简单很多同学第一次做商城类项目会先瞄一眼“有哪些页面”小程序首页、商品分类、详情页、购物车、结算页、订单列表管理后台再配商品管理、订单管理、用户管理。这些页面加起来看上去就是一个标准后台管理系统加一个小程序壳子好像把数据表建好再做几个增删改查接口就够了。真正进入联调阶段才会发现页面能展示商品只是最外层。电商系统最麻烦的部分是所有“写操作”都存在状态流转用户先加购物车再下单然后支付接着库存要减订单要改成已支付如果支付失败订单要关闭库存还得加回来。这一串操作不能靠前端页面顺序“故意配合”来实现因为接口是可以被并发请求、重复点击甚至恶意脚本触发的。所以你在做这样一个项目时真正要训练的不是增删改查语法而是三件事第一接口权限怎么控制用户不能越权查别人的订单第二关键操作怎么加事务下单和扣库存要么都成功要么都失败第三金额计算以服务端为准而不是轻信小程序传过来的“总价”。能把这三点讲明白这个项目才称得上“精品”。1.2 珠宝首饰品类给项目增加了哪些难度如果做的是服装、数码或图书商城商品规格很简单基本一个商品对应一个价格就行。珠宝首饰则不同它的商品维度要复杂很多。一条项链可能有 18K 金、铂金、玫瑰金等材质差异戒托颜色有差异钻石参数有重量、颜色、净度、切工的区别戒指还得有圈号。用户在详情页里每切换一个“圈号”或“材质”页面上的价格和库存都在变。这意味着项目在设计数据表时就必须把“商品”和“商品规格”拆开而不是在一张表里塞一大堆字段。从工程角度引入 SPU 和 SKU 的概念是必然选择同时高客单价决定了库存数量不能扣错否则一个 0.1 克拉钻戒卖出两份损失远高于普通日用品。因此珠宝首饰很适合用来打磨电商底层建模能力它迫使你认真处理规格、价格、库存、订单快照这些看起来基础、实际很容易出差错的细节。1.3 什么样的读者适合读这篇文章如果你是准备毕业设计、课程设计或者想找一份“小程序 管理后台 Spring Boot 后端”全栈练习项目的开发者这篇文章适合你。它不要求你已经精通微服务但希望你最好了解 Spring Boot 的基本注解、Vue3 的组件思维和微信小程序的页面结构。如果你已经做过普通商城正处于“业务能跑但总觉得代码很脆”的阶段这篇文章同样有参考价值。它会重点讲事务边界、库存扣减、价格快照、幂等和权限这些不是锦上添花而是从演示项目走向可用系统的分水岭。文章后面给出的建表语句和核心代码都是一套“最小但完整”的参考骨架你可以把它当作起点再补全自己需要的功能。2. 系统架构与核心概念三端如何协作2.1 三层架构与组件职责一类典型的珠宝首饰交易系统会把项目拆成三个端微信小程序端C 端用户 -- HTTPS / JSON -- Spring Boot 服务端 | ------------------ | | MySQL Redis 业务数据 缓存/登录态/购物车 Vue3 管理后台运营管理员 -- HTTPS / JSON -- Spring Boot 服务端在这个结构里微信小程序负责触达用户做浏览、加购、下单、支付、订单查询页面Vue3 管理后台面向运营和客服负责商品上下架、库存维护、分类管理、订单发货和售后处理Spring Boot 服务端是所有业务规则的“裁判”职责包括微信登录态换取、用户身份鉴权、商品查询、购物车管理、下单事务、支付回调处理和后台管理接口。这样拆分的好处是各端边界清楚。商城页面的 UI 可以用 Vue3 生态继续扩展也能用小程序原生语法持续维护管理后台和后端是独立工程部署时可以分别上线。对毕设或简历项目来说这种“两套前端 一套后端”的组合也能比较完整地展示全栈能力。2.2 SPU 与 SKU珠宝商品建模的关键SPU 是“标准产品单元”可以理解成商品详情页里展示的那件“货”。例如“18K 金钻石戒指 经典六爪款”商品主图、详情图文、证书编号这类公共属性挂在 SPU 上。SKU 是“库存量单位”是用户真正能加入购物车、能下单购买的最小单元。同一个 SPU 下因为“圈号 15”和“圈号 16”不同或“18K 金”和“铂金”不同会有多个 SKU。用户看到的库存、价格都应该取 SKU 上的值。层级含义示例主要字段SPU商品款式/详情页主体18K金钻石戒指经典款商品名、类目、主图、详情、状态SKU具体可下单的规格组合18K金/0.5克拉/H色/圈号15SKU编号、规格名、价格、库存Category商品分类戒指、项链、手镯、耳饰分类名、父级、排序数据库里通常是category - spu - sku的层级关系。SPU 下的 SKU 数量不需要做得很夸张但至少要在后台管理界面支持“一个商品下添加多个规格每个规格不同价格/库存”这样才能应对珠宝首饰的真实需求。2.3 订单、库存与支付状态的一致性交易系统的状态流可以简化成待支付 - 已支付 - 已发货 - 已完成 \- 已关闭用户在结算页点击“提交订单”服务端要做的不只是往订单表插入一条记录。它还要校验 SKU 是否有效、库存是否足够并按服务端数据库中 SKU 的最新价格重新计算总金额。生成订单的同时库存要扣减如果用户一直没有支付超过一定时间后订单关闭库存需要回补。真正的微信支付回调是异步的支付平台会向后端通知支付结果后端必须验签、更新支付流水、修改订单状态并且保证“同一次支付通知被多次请求时不会重复发货”。对于课程项目和毕设演示来说可以先实现“模拟支付”把订单直接从未支付改为已支付但你仍然要理解真实流程里状态机为什么要存在。这个理解一旦到位后面接真实支付对接时代码骨架就不需要推翻重写。3. 环境准备与工程骨架动手之前先把环境统一好。以下工具是当前这套“Spring Boot Vue3 微信小程序”技术栈比较常见的选择。Spring Boot 版本如果项目使用的是 3.x本文代码思路依然成立如果使用 Spring Boot 4.x建议按官方正式版调整依赖版本示例代码中的业务逻辑不会发生根本变化。端环境要求说明后端JDK 17 或 21Maven 3.6推荐 LTS 版本数据库MySQL 8.0Redis 6MySQL 存业务数据Redis 存登录态/缓存Vue3 后台Node 18npm 或 pnpmVite 项目Vue3 Pinia Vue Router小程序微信开发者工具最新稳定版需要小程序 AppID测试可申请测试号IDEIDEA 或 VS Code后端和前端可分开打开目录结构建议这样规划jewelry-mall ├── server # Spring Boot 后端工程 │ ├── src/main/java │ ├── src/main/resources │ └── pom.xml ├── admin-web # Vue3 管理后台工程 │ ├── src/api │ ├── src/views │ └── vite.config.js ├── miniapp # 微信小程序工程 │ ├── pages │ ├── utils/request.js │ └── app.js └── sql # 数据库初始化脚本这里要提醒一个容易踩坑的点不要一开始就追求“分布式”或“微服务”先用单工程把业务跑通更实际。管理后台和小程序访问的是同一个后端接口只是接口路径上有/api/mall和/api/admin之类的区分。这样既能完成业务闭环又不会让项目复杂度失控。数据库初始化脚本放sql目录好处是有完整的建表记录换一台电脑或给评审老师演示时可以很快重建数据库。后面第 4 章的表结构也会围绕这个工程结构展开。4. 数据库与核心表设计4.1 核心表清单电商系统表不要一开始铺得太多。围绕最小闭环核心表可以这样设计表名表的作用memberC 端用户保存 openid、昵称、头像、手机号、状态admin_user后台管理员账号保存用户名、密码密文、角色category珠宝分类支持多级分类spu商品主体保存名称、分类、主图、详情、上下架状态sku商品规格保存规格名、价格、库存cart_item购物车项可考虑放在 Redis也可落库orders订单主表保存订单号、用户、总金额、状态、地址快照order_item订单明细保存下单瞬间的 SKU 名称、单价、数量、图片payment_record支付流水保存支付方式、流水号、金额、状态这里特别说明一下为什么要用orders而不是orderorder在部分数据库里是关键字或半保留字直接用容易引发 SQL 语法问题。表名order_item没有这个问题但为了风格统一订单主表可以写成orders订单明细沿用order_item。这个细节在 MySQL 中很常见尽早避开可以省去很多排查时间。4.2 商品表与 SKU 表商品表可以这样设计基础字段CREATE TABLE spu ( id BIGINT PRIMARY KEY AUTO_INCREMENT, category_id BIGINT NOT NULL COMMENT 分类ID, name VARCHAR(128) NOT NULL COMMENT 商品名称, sub_title VARCHAR(255) DEFAULT COMMENT 卖点副标题, main_image VARCHAR(255) DEFAULT COMMENT 主图URL, detail_images TEXT COMMENT 详情图片JSON数组或逗号分隔, status TINYINT NOT NULL DEFAULT 0 COMMENT 0下架 1上架, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT珠宝商品SPU表;SKU 表需要重点关注“唯一性”和“库存”CREATE TABLE sku ( id BIGINT PRIMARY KEY AUTO_INCREMENT, spu_id BIGINT NOT NULL COMMENT 所属SPU ID, sku_no VARCHAR(64) NOT NULL COMMENT SKU编码用于商家内部识别, spec_name VARCHAR(128) NOT NULL COMMENT 规格描述例如18K金/0.5克拉/圈号15, material VARCHAR(32) DEFAULT COMMENT 材质, carat DECIMAL(8,2) DEFAULT NULL COMMENT 主石克拉数, price DECIMAL(10,2) NOT NULL COMMENT 销售价单位元, stock INT NOT NULL DEFAULT 0 COMMENT 可售库存, status TINYINT NOT NULL DEFAULT 1 COMMENT 1启用 0停用, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_sku_no (sku_no), KEY idx_spu_id (spu_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT珠宝商品SKU表;商品价格用DECIMAL不要用double。浮点数在金额计算中会产生精度问题后续 Java 端配合BigDecimal使用。珠宝商品的材质、克拉数等字段不是所有品类都适用属于典型的扩展字段实际项目中可以使用JSON列或按商品类型扩展子表不必追求“一个表容纳所有商品属性”。4.3 订单表与订单明细订单表是否包含“地址快照”“商品快照”是区分“纯学习项目”和“接近生产项目”的重要标志。用户下单之后商家改动商品名、价格、库存都不应该影响历史订单所以订单关联的数据必须复制一份快照。CREATE TABLE orders ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL COMMENT 业务订单号, member_id BIGINT NOT NULL COMMENT 下单用户ID, total_amount DECIMAL(10,2) NOT NULL COMMENT 订单总金额, pay_amount DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT 实付金额, status TINYINT NOT NULL DEFAULT 0 COMMENT 0待支付 1已支付 2已发货 3已完成 4已取消, receiver_name VARCHAR(64) NOT NULL, receiver_phone VARCHAR(32) NOT NULL, receiver_address VARCHAR(255) NOT NULL, remark VARCHAR(255) DEFAULT , create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, pay_time DATETIME DEFAULT NULL, UNIQUE KEY uk_order_no (order_no), KEY idx_member_id (member_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单主表;CREATE TABLE order_item ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_id BIGINT NOT NULL, spu_id BIGINT NOT NULL, spu_name VARCHAR(128) NOT NULL COMMENT 商品名称快照, sku_id BIGINT NOT NULL, sku_no VARCHAR(64) NOT NULL, spec_name VARCHAR(128) NOT NULL COMMENT 规格快照, main_image VARCHAR(255) DEFAULT COMMENT 主图快照, price DECIMAL(10,2) NOT NULL COMMENT 成交单价快照, quantity INT NOT NULL COMMENT 购买数量, total_amount DECIMAL(10,2) NOT NULL COMMENT 商品小计, KEY idx_order_id (order_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单明细表;订单号的生成建议使用“日期 随机序列 业务标识”尽量不要用自增主键直接暴露给用户避免被遍历。更严格的做法是使用雪花算法或 Redis 发号器生成订单号但对于单体课程项目来说只要保证唯一且有规则即可。订单明细表保存了商品名称、SKU 名称、主图、单价快照后面做售后、对账、运营统计时都会非常方便。5. 后端服务登录、商品、购物车与下单事务5.1 基础配置先看后端核心配置文件。假设项目使用 MySQL 和 Redis连接的账号密码以自己的本地环境为准# 文件路径server/src/main/resources/application.yml server: port: 8080 spring: application: name: jewelry-mall-server datasource: url: jdbc:mysql://localhost:3306/jewelry_mall?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver data: redis: host: localhost port: 6379 password: database: 0 mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0MySQL 连接地址里的useSSLfalse适合本地开发生产环境通常应开启 SSL 或使用云数据库默认的安全连接方式。Redis 如果不需要密码本地开发可以留空如果服务器上配置了密码必须填入并妥善管理避免明文密码出现在配置仓库中。5.2 微信登录接口小程序端不能自己完成身份识别最常用的方式是wx.login拿到临时code再把code发给后端。后端拿到code后去向微信接口换openid和session_key。这里有两个关键安全点第一小程序appSecret只能保存在后端不能写进小程序代码第二code有效期很短且只能使用一次后端换到openid后要自己签发业务 token 给小程序端。// 文件路径server/src/main/java/com/example/mall/service/WxAuthService.java Service public class WxAuthService { Value(${wx.appid}) private String appid; Value(${wx.secret}) private String secret; Resource private MemberMapper memberMapper; Resource private StringRedisTemplate stringRedisTemplate; public LoginVO wxLogin(String code) { // 1. 调用微信接口code 换 openid WxSessionResp resp wxCode2Session(code); if (resp null || resp.getOpenid() null) { throw new BizException(微信登录失败); } // 2. 查询或创建会员 Member member memberMapper.findByOpenid(resp.getOpenid()); if (member null) { member new Member(); member.setOpenid(resp.getOpenid()); member.setStatus(1); memberMapper.insert(member); } // 3. 生成业务token并写入Redis设置有效期 String token UUID.randomUUID().toString().replace(-, ); stringRedisTemplate.opsForValue().set( login:token: token, String.valueOf(member.getId()), 7, TimeUnit.DAYS ); return new LoginVO(token, member.getId()); } }注意真实的code2Session调用需要基于RestTemplate、WebClient或OkHttp发起 GET 请求到微信服务端示例中省略了 HTTP 细节只保留了主流程。很多初学者会在这一步把session_key原样返回给前端这其实是安全风险因为session_key用于解密敏感数据不应该暴露到业务端。更合理的做法是后端在需要解密手机号或敏感信息时才临时使用session_key用完即弃。5.3 下单事务与库存扣减下单是最容易出现“看起来成功、实际数据错误”的接口。下面用一段典型的OrderService.createOrder表达核心思路不信任前端传的金额重新查 SKU 价格更新库存时使用stock quantity作为条件防止库存被扣成负数整个方法处于事务中任何异常都会整体回滚。// 文件路径server/src/main/java/com/example/mall/service/OrderService.java Service public class OrderService { Resource private OrderMapper orderMapper; Resource private SkuMapper skuMapper; Transactional(rollbackFor Exception.class) public Long createOrder(OrderCreateDTO dto) { // 1. 查询SKU不存在则直接抛异常 Sku sku skuMapper.selectById(dto.getSkuId()); if (sku null || sku.getStatus() ! 1) { throw new BizException(商品规格无效); } // 2. 原子扣库存UPDATE sku SET stock stock - ? WHERE id ? AND stock ? int rows skuMapper.deductStock(dto.getSkuId(), dto.getQuantity()); if (rows 0) { throw new BizException(库存不足); } // 3. 以服务端查询到的SKU价格为准计算订单金额 BigDecimal skuPrice sku.getPrice(); BigDecimal totalAmount skuPrice.multiply( new BigDecimal(dto.getQuantity())); // 4. 写订单主表和明细表保存价格/商品快照 Orders order new Orders(); order.setOrderNo(generateOrderNo()); order.setMemberId(dto.getMemberId()); order.setTotalAmount(totalAmount); order.setPayAmount(totalAmount); order.setStatus(0); orderMapper.insert(order); OrderItem item new OrderItem(); item.setOrderId(order.getId()); item.setSpuId(sku.getSpuId()); item.setSkuId(sku.getId()); item.setSkuNo(sku.getSkuNo()); item.setSpecName(sku.getSpecName()); item.setPrice(skuPrice); item.setQuantity(dto.getQuantity()); item.setTotalAmount(totalAmount); orderItemMapper.insert(item); // 5. 清除购物车中对应项购物车表存在时 cartMapper.deleteByMemberIdAndSkuId(dto.getMemberId(), dto.getSkuId()); return order.getId(); } }这里的deductStock对应 SQL 可以写成UPDATE sku SET stock stock - #{quantity} WHERE id #{skuId} AND stock #{quantity}先扣库存后写订单的方式适合单 SKU 直购场景。如果购物车里有多个商品下单时需要遍历多个 SKU 并计算总价。无论哪种场景关键都是把“扣库存”放在 WHERE 条件中判断而不是先SELECT stock再在 Java 里比较。后者在并发请求打过来时可能读出同一个旧库存导致超卖。还要特别强调事务方法内部不能把异常“吞掉”。有些同学喜欢在事务方法里try-catch后打印日志然后返回成功这会破坏Transactional的回滚机制。正确做法是让校验异常继续向上抛由全局异常处理器统一转换为错误响应。6. Vue3 管理后台核心模块6.1 请求封装与登录态维护Vue3 管理后台不是小程序本身而是运营人员操作的网页。它通常采用 Vue3 Vite Pinia Vue Router 的技术组合。为了让每个请求自动携带管理员 token并且统一处理 401 登录过期通常会把 axios 封装一层// 文件路径admin-web/src/api/request.js import axios from axios import { ElMessage } from element-plus import router from /router const request axios.create({ baseURL: /api/admin, timeout: 10000 }) request.interceptors.request.use(config { const token localStorage.getItem(admin_token) if (token) { config.headers.Authorization Bearer ${token} } return config }) request.interceptors.response.use( response { const res response.data if (res.code ! 0) { ElMessage.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) } return res }, error { if (error.response error.response.status 401) { localStorage.removeItem(admin_token) router.push(/login) } return Promise.reject(error) } ) export default request封装拦截器之后页面代码就不需要每个接口重复处理错误。需要注意的是生产环境里baseURL通常指向网关或后端域名而不是简单的/api/admin在开发环境可以通过 Vite 的 proxy 配置把/api转发到http://localhost:8080避免跨域问题。登录态 token 放在localStorage只是相对简单的方案如果要兼顾安全性可以结合后端设置 HttpOnly Cookie并增加刷新令牌机制。6.2 商品管理页面管理后台最核心的功能是商品上下架、SKU 维护和库存修改。用 Vue3 的script setup写一个商品列表页面骨架!-- 文件路径admin-web/src/views/spu/SpuList.vue -- template div el-table :datalist border el-table-column propid labelID width80 / el-table-column propname label商品名称 / el-table-column propcategoryName label分类 width140 / el-table-column propstatus label状态 width100 template #default{ row } el-tag :typerow.status 1 ? success : info {{ row.status 1 ? 上架 : 下架 }} /el-tag /template /el-table-column el-table-column label操作 width220 template #default{ row } el-button typeprimary link clickonEdit(row)编辑SKU/el-button el-button typewarning link clickonToggle(row) {{ row.status 1 ? 下架 : 上架 }} /el-button /template /el-table-column /el-table /div /template script setup import { ref, onMounted } from vue import { ElMessage } from element-plus import request from /api/request const list ref([]) async function fetchList() { const res await request.get(/spu/page) list.value res.data.records } function onEdit(row) { // 跳转到 SPU 编辑页或打开SKU维护弹窗 } async function onToggle(row) { const action row.status 1 ? off : on await request.put(/spu/status, { id: row.id, action }) ElMessage.success(操作成功) fetchList() } onMounted(fetchList) /script这个页面只是商品管理的一部分完整的“编辑 SKU”还需要维护规格名称、价格、库存、规格展示图片等。设计后台接口时建议把“保存 SPU”和“批量保存 SKU”拆成清晰的接口语义比如POST /api/admin/spu和PUT /api/admin/spu/{spuId}/skus这样前端操作时可以分批提交不至于一个超大表单把所有数据混在一起。管理后台在真实项目中还应包含分类管理、订单管理、会员管理、售后/退款、数据统计等模块。它们都属于典型的“后台 CRUD 状态流转”但要把规则写对后端接口仍然不能只是简单暴露数据库字段。7. 微信小程序商城端实现7.1 小程序请求封装与登录小程序端如果使用原生语法目录中会有pages、utils、app.js等。由于小程序自身没有axios一般封装wx.request。建议封装成一个request函数自动处理登录逻辑和统一响应// 文件路径miniapp/utils/request.js const BASE_URL https://your-api.example.com/api/mall function request({ url, method GET, data {} }) { return new Promise((resolve, reject) { wx.request({ url: ${BASE_URL}${url}, method, data, header: { Content-Type: application/json, Authorization: wx.getStorageSync(token) || }, success(res) { if (res.data res.data.code 0) { resolve(res.data.data) } else { // 401 可在这里触发重新登录 reject(res.data) } }, fail(err) { reject(err) } }) }) } function login() { return new Promise((resolve, reject) { wx.login({ success: async res { const { code } res const data await request({ url: /auth/wx-login, method: POST, data: { code } }) wx.setStorageSync(token, data.token) wx.setStorageSync(memberId, data.memberId) resolve(data) }, fail: reject }) }) } module.exports { request, login }实际开发时业务域名的 HTTPS 证书和小程序后台的合法域名配置是绕不开的步骤。小程序真机上不允许直接访问http://localhost或明文 HTTP 接口开发者工具里可以临时勾选“不校验合法域名”但演示或上线前必须在微信公众平台配置 HTTPS 域名并且后端域名必须已经备案。对于本地联调可以使用内网穿透工具将本地服务暴露成临时 HTTPS 地址但要注意保护调试通道联调结束及时关闭。7.2 商品浏览、下单与支付状态推进小程序端页面通常包括首页、分类、商品详情、购物车、结算、订单列表。用户进入商品详情页时页面会展示 SKU 选择和实时价格所以详情接口建议一次返回当前 SPU 下的启用 SKU 列表减少多次请求带来的延迟和价格闪烁。提交支付的交互要特别小心下单成功只是创建了“待支付订单”并不代表支付完成。用户在结算页点击“立即支付”后小程序会调用后端的下单接口然后根据返回的订单号去调起微信支付。真实对接时后端需要先调用微信“统一下单”拿到paySign等参数返回给小程序小程序再通过wx.requestPayment完成支付。支付结果最终以后端收到微信异步回调为准而不能只想根据小程序前端success回调来判定。对于课程项目和演示来说如果暂时没有企业支付资质可以采用“模拟支付”模式在结算页提示“演示环境点击后直接支付”后端把订单状态改为已支付。但这必须在文档中说明模拟支付没有微信验签只适合用来跑通业务全流程正式上线前必须替换成真实微信支付并且服务端要增加回调验签、回调幂等处理。8. 联调运行效果与问题排查8.1 启动与运行顺序一个完整项目跑通建议按照“数据库 - 后端 - 管理后台 - 小程序”的顺序启动。第一步创建数据库并导入 SQL。在 MySQL 中执行建库和建表语句确认表已生成。第二步启动 Redis并确认端口可连接。第三步启动 Spring Boot 工程cd server mvn spring-boot:run启动成功后可以先用接口测试工具请求健康检查或商品列表接口。第四步启动 Vue3 管理后台cd admin-web npm install npm run dev