ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

SpringBoot4+Vue3+微信小程序:化妆品交易商城全栈开发实践

2026/9/4 3:17:14 拓冰建站 浏览量
SpringBoot4+Vue3+微信小程序:化妆品交易商城全栈开发实践 很多同学拿到“化妆品交易小程序”这类题目时第一反应是这不就是一个带购物车的 CRUD 吗前端小程序能浏览、加购、下单后端管商品和订单再配一个 Vue3 管理后台管上架和发货看起来并不难。但真正动手后才发现化妆品类目的小程序商城有大量“看不见”的工作微信登录态怎么换、商品规格和库存怎么扣、订单状态怎么流转、后台权限怎么隔离、图片上传怎么处理以及小程序发布前各类配置为什么总在报错。这篇文章不打算堆“精品项目”的界面截图而是以 SpringBoot4 Vue3 微信小程序商城的组合为主线把一套化妆品交易小程序从技术选型、模块划分到核心接口实现、运行验证和常见坑位完整拆一遍。读完你会得到一个清晰的开发路线也能避开很多学生在毕设或练手项目中反复踩的坑。1. 这套技术组合真正解决了什么问题从项目标题看关键词是“化妆品交易小程序”“化妆品电商平台”“Vue3 管理后台”。如果只看表面很多人会误以为这套项目重点在首页装修和商品展示实际上它真正要解决的是三类工程问题第一多端数据一致问题。用户在小程序端看到的价格、库存、优惠信息必须和 Vue3 管理后台编辑的结果一致。这个一致性不是“两边连同一个数据库”就能天然保证的还要处理缓存、并发下单、商品上下架状态同步。第二微信生态对接问题。小程序不是普通 H5它需要 wx.login 获取 code再通过后端调用微信接口换 openid 和 session_key支付时涉及签名与回调验签发布时还有域名、业务域名、跳转权限等限制。第三运营后台复杂性问题。管理端不只是“增删改查”还要处理管理员登录、滑块验证、权限区分比如运营只看订单、管理员才能改商品、订单发货、库存回滚。这套组合最大的价值在于用一个后端同时支撑小程序端和管理后台端避免维护两套接口用 Vue3 做后台是因为组件化开发在电商后台这种“表单密集 表格密集 状态联动”的场景下确实比传统多页模板效率高。所以这不是选题时髦而是技术结构本身适合电商项目。从实际开发角度判断这个项目最适合三类人正在做电商类毕业设计的学生想练习“小程序 管理后台 服务端”全栈流程的开发者以及需要快速搭建垂直品类商城原型的产品或技术负责人。2. 化妆品电商小程序的项目架构与核心模块先不写代码我们把整套系统的边界画清楚。一个完整的化妆品交易平台通常包含三个客户端端侧技术栈主要职责典型页面微信小程序端原生小程序 / uni-app用户浏览、搜索、加购、下单、支付、查看订单首页、分类、购物车、订单列表、个人中心Vue3 管理后台Vue3 Vite Element Plus商品管理、分类管理、订单处理、营销配置、数据统计登录页、商品列表、订单详情、仪表盘SpringBoot 服务端SpringBoot MyBatis-Plus / JPA提供 REST API、微信登录、支付回调、权限校验无界面只输出 JSON前后端分离是这套项目的基本前提。小程序的 API 请求和管理后台的 API 请求通常打到同一个服务端但接口路径和鉴权策略需要区分。比较常见的做法是/api/user/** 小程序端接口携带用户 token /api/admin/** 管理后台接口携带管理员 token为什么要把两个端放在同一个后端里对于课程设计和中小型项目来说拆成两个独立微服务反而增加部署和联调成本。一个 SpringBoot 应用内用拦截器区分用户角色和管理员角色已经足够支撑中小规模化妆品商城的业务量。从电商业务角度核心模块可以拆成这些会员模块微信登录、手机号授权、收货地址、积分等级商品模块分类、品牌、SPU/SKU、规格、图片、库存购物车与订单模块加购、下单、库存扣减、订单状态机、售后营销模块优惠券、满减、秒杀、新品推荐支付模块微信支付下单、回调验签、退款申请管理后台模块管理员登录、图表统计、商品上下架、订单发货化妆品类目还有一个容易忽略的点商品往往有“规格”比如色号、容量价格还可能随规格变化。数据库设计时必须把商品表、规格表和库存表分开不能简单地在商品表里写一个 price 字段。3. 从用户下单链路看核心流程理解模块之后还要从用户视角把所有模块串起来。一次完整的化妆品购买流程在后端眼里是这样的用户打开小程序进入首页看到推荐商品列表。点击某款口红看到不同色号不同价格。选择色号加入购物车此时后端需要校验SKU 是否存在、是否上架、库存是否足够。用户进入购物车提交订单后端创建待支付订单并锁住对应库存。用户调用微信支付微信支付服务器回调后端支付通知接口。后端校验回调签名将订单状态改为已支付。管理员在 Vue3 后台看到新订单执行发货操作。用户确认收货后订单状态变为已完成。这里最容易出问题的不是控制器怎么写而是第 4 步和第 6 步之间的状态一致性。先看下单减库存。很多初学者会在前端拿到商品后直接“数量减一”一旦用户下单后没支付库存就凭空消失了。正确做法是下单时锁定库存支付成功后扣减真实库存超时未支付时释放锁定库存。如果项目规模不大可以用订单表加一个 status 字段配合定时任务实现不必一上来就引入分布式事务。再看微信支付回调。这里的关键原则是不能信任小程序前端传来的“支付成功”状态必须以微信服务器回调后端接口的结果为准。开发者最常见的错误是直接在小程序端写死了支付成功的跳转页面后端订单状态却没更新。这一点在测试时不容易发现一旦真正接入支付就会出现“用户付了钱后台还显示待支付”的问题。整个流程串起来之后你会发现化妆品交易小程序并不是“功能少”而是“流程环节多”。这也是为什么技术选型要选前后端分离架构每条链路都可以单独调试单独验证。4. 服务端核心接口设计与关键技术点服务端是整个商城的中枢。设计接口时我建议按资源而不是按页面来规划。商品资源、订单资源、用户资源各归一类这样小程序端和 Vue3 管理后台都能复用。先定义统一返回结构。电商项目的前后端接口数量通常在几十个以上如果每个接口的返回格式都不一样前端解析会非常痛苦。推荐定义一个泛型包装类// 文件路径src/main/java/com/example/mall/common/ApiResult.java public class ApiResultT { private Integer code; private String message; private T data; public static T ApiResultT success(T data) { ApiResultT result new ApiResult(); result.code 200; result.message ok; result.data data; return result; } public static T ApiResultT error(Integer code, String message) { ApiResultT result new ApiResult(); result.code code; result.message message; return result; } // getter / setter 省略 }接口返回示例统一是{ code: 200, message: ok, data: {} }商品列表核心接口并不复杂重点在于分页和搜索条件组合// 文件路径src/main/java/com/example/mall/controller/ProductController.java RestController RequestMapping(/api/user/product) public class ProductController { GetMapping(/page) public ApiResultPageResultProductVO page( RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer size, RequestParam(required false) Long categoryId, RequestParam(required false) String keyword) { // 调用 service 查询商品分页返回上架状态商品 // 这里实际项目会使用 MyBatis-Plus 的 LambdaQueryWrapper return ApiResult.success(productService.pageProducts(page, size, categoryId, keyword)); } }很多同学会把分页参数写在请求体里这是不符合 REST 习惯的。查询类接口建议用 GET 请求 query 参数写操作才用 POST。这样做的好处是小程序端、管理后台和接口测试工具三方调试都很方便。服务端还有一个绕不开的问题管理后台登录和用户登录要分开。普通用户用的是微信 openid 体系管理员使用账号密码体系。如果把两套用户混在一张表里后患无穷。管理员登录后签发独立的 adminToken用户登录后签发 userToken两个拦截器分别校验这是比较稳妥的设计。4.1 关于 SpringBoot 版本的一点说明项目标题写的是 SpringBoot4但从当前社区实际来看大家更常用的是 Spring Boot 3.x 系列稳定版。如果电脑上还没有 Spring Boot 环境不必纠结具体大版本号直接从 Spring Initializr 生成一个工程依赖选择 Spring Web、Validation、MyBatis 相关依赖即可。Spring Boot 3 从启动方式到注解模型与后续 4.x 的思路是连贯的写代码时把依赖版本交给 Maven 管理而不是手动指定一个不确定的版本。遇到类似“SpringBoot4 如何对接滑块验证码”的问题处理思路也和新旧版本关系不大服务端生成验证码 ID 和图片前端完成滑动后提交带验证码 ID 的校验请求服务端二次确认通过后才走真正的业务接口。4.2 用户登录接口设计用户第一次打开小程序时需要先静默登录。小程序前端拿 wx.login 返回的 code发给后端后端再拿着 code 加上小程序的 appId 和 secret 去微信服务器换 openid。这个换 openid 的操作必须在后端完成绝对不要把 secret 写进小程序代码里。// 文件路径src/main/java/com/example/mall/controller/AuthController.java RestController RequestMapping(/api/user/auth) public class AuthController { PostMapping(/wx-login) public ApiResultLoginVO wxLogin(RequestBody WxLoginDTO dto) { // 1. 调用微信接口使用 dto.getCode() 换取 openid // 2. 根据 openid 查询用户不存在则注册新用户 // 3. 生成自定义登录态 token缓存到 Redis 并设置过期时间 // 4. 返回 token 给小程序端 return ApiResult.success(loginVO); } }实际项目中为了调试方便小程序后端接口可以预留一个测试登录入口仅在本地测试环境开启。这样你在没有真机微信环境时也能通过 Postman 跑通业务流程而不用每次打开小程序测试。5. 小程序端开发从登录态到商品列表小程序端代码虽然写在微信开发者工具里但真正难的不是 wxml 模板而是登录态的管理和页面数据的加载。对于化妆品商城这类需要下单的小程序登录是所有业务的前提。一个小程序端登录示例// 文件路径miniprogram/utils/auth.js function wxLogin() { return new Promise((resolve, reject) { wx.login({ success: async (res) { if (res.code) { try { const loginRes await wx.request({ url: https://your-domain.com/api/user/auth/wx-login, method: POST, data: { code: res.code } }); // 将后端返回的 token 存入 storage wx.setStorageSync(token, loginRes.data.data.token); resolve(loginRes.data.data); } catch (err) { reject(err); } } else { reject(new Error(wx.login 失败)); } }, fail: reject }); }); } module.exports { wxLogin };注意wx.request 在小程序里需要配置域名白名单同时在开发者工具中勾选“不校验合法域名”才能在本地联调。正式发布前必须把接口域名换成已备案并通过小程序后台配置的 HTTPS 域名否则真机上会直接请求失败。首页商品列表的 wxml 不需要太多技巧但要注意图片懒加载和加载状态!-- 文件路径miniprogram/pages/index/index.wxml -- view classproduct-grid view wx:for{{productList}} wx:keyid classproduct-card bindtapgoDetail >// 文件路径src/utils/request.js import axios from axios; import { ElMessage } from element-plus; const request axios.create({ baseURL: /api, timeout: 10000 }); // 请求拦截器自动携带管理员 token request.interceptors.request.use((config) { const token localStorage.getItem(adminToken); if (token) { config.headers.Authorization Bearer ${token}; } return config; }); // 响应拦截器统一处理 code request.interceptors.response.use( (response) { const res response.data; if (res.code ! 200) { ElMessage.error(res.message || 请求失败); return Promise.reject(new Error(res.message)); } return res; }, (error) { ElMessage.error(error.response?.data?.message || 网络异常); return Promise.reject(error); } ); export default request;为什么要统一封装因为管理后台几十个页面都需要判断 code如果每个页面都写一遍代码会迅速失控。而且拦截器可以统一处理 token 过期情况比如 401 时自动跳转到登录页。6.2 商品管理页的 Vue3 组件写法管理后台业务中商品管理页是典型代表上方搜索表单中间表格底部弹出编辑抽屉。用 Vue3 的script setup写起来比 options API 更精简。!-- 文件路径src/views/product/ProductList.vue -- template div el-card el-input v-modelquery.keyword placeholder搜索商品名称 clearable stylewidth: 220px / el-button typeprimary clickloadProducts查询/el-button el-button typesuccess clickopenEdit新增商品/el-button /el-card el-table :dataproductList v-loadingloading el-table-column propid labelID width80 / el-table-column propname label商品名称 / el-table-column propprice label价格 width120 / el-table-column propstock label库存 width120 / el-table-column label操作 width180 template #default{ row } el-button sizesmall clickopenEdit(row)编辑/el-button el-button sizesmall typedanger clickhandleDelete(row)下架/el-button /template /el-table-column /el-table /div /template script setup import { ref, onMounted } from vue; import request from /utils/request; const query ref({ keyword: }); const productList ref([]); const loading ref(false); async function loadProducts() { loading.value true; try { const res await request.get(/admin/product/page, { params: query.value }); productList.value res.data.records; } finally { loading.value false; } } function openEdit(row) { // 打开编辑抽屉row 有值则是编辑否则是新增 } function handleDelete(row) { // 调用下架接口注意这里多数是软删除/上下架不建议物理删除 } onMounted(() { loadProducts(); }); /script这里在 Vue3 中很容易习惯性地写 this.$refs 或 this.$message但实际上 script setup 中默认没有 this所有响应式变量都要显式定义。如果你是从 Vue2 转过来的这是第一个需要适应的点。6.3 管理后台的路由与权限控制管理后台通常有商品管理、订单管理、分类管理、用户管理、数据统计等页面路由直接在 Vue Router 中配置。如果项目有“普通运营”和“超级管理员”两类角色还需要根据权限字段过滤路由。对于课程设计或中小项目可以先在路由守卫里检查是否登录细粒度权限可以后面再加避免一上来就陷入权限设计的复杂性。7. 安全防护滑块验证码与接口防刷从搜索热词可以看出“springboot4 如何对接 tianai 滑块”是很多人关心的问题。化妆品商城的管理后台是运营人员操作的核心入口登录接口如果被脚本刷轻则密码被暴力破解重则影响后台稳定性。常见的做法是引入行为式滑块验证码登录时先完成滑块再把验证凭据连同账号密码一起提交给服务端。集成思路大致分三步后端为滑块验证码生成一个唯一验证码 ID并返回给前端。前端拿到滑块图片用户拖动完成后把验证结果回传给后端后端校验通过后标记该验证码 ID 为“已验证”。登录时后端再次校验验证码 ID确认确实执行过滑块否则拒绝登录。这里的关键是“二次校验”。如果只在前端判断滑块是否通过脚本完全可以绕过前端直接调用登录接口。所以必须让后端参与校验并把滑块验证码与登录请求绑定。放到 SpringBoot 工程里就是新增一个 CaptchaController提供 get 和 check 两个接口。由于 tianai-captcha 这类组件版本迭代较快具体依赖坐标和初始化参数建议参考当前官方仓库但交互链路是固定的理解链路比背代码更重要。这个小节其实适用于整个项目不只是登录。管理后台的图片上传也可以加验证防止恶意文件上传商品详情页阅读频繁时也可以加频率限制。安全不是做完业务后的附加项而是在写接口时就要考虑的部分。8. 运行验证从本地起服务到真机预览无论代码怎么写最终都要跑起来验证。建议按以下顺序执行启动数据库创建 mall 数据库执行项目里的初始化 SQL。没有初始化 SQL 的情况下要先建业务表这一步最容易因为字段类型不一致导致后续报错。启动 SpringBoot 服务端。看控制台日志是否出现 Tomcat started确认端口没被占用。如果端口被占用在 application.yml 里修改 server.port。启动 Vue3 管理后台。在项目目录执行npm install再执行npm run dev浏览器打开 http://localhost:5173 查看是否能打开登录页。打开微信开发者工具导入小程序项目AppID 可以先使用测试号。若后端运行在本地且未配置域名需要在“详情-本地设置”中勾选“不校验合法域名”。在小程序首页请求商品列表查看 Network 面板确认接口返回 code 200数据正常渲染出来。预期输出示例{ code: 200, message: ok, data: { records: [ { id: 1, name: 保湿精华液, price: 199.0, stock: 100 } ], total: 1 } }成功判断标准非常简单小程序端能看到商品列表数据管理后台能对这条商品做编辑刷新小程序端后数据变化同步。到这里前端、后端、数据库三端链路就全部打通了。如果接口请求失败优先打开微信开发者工具的 Network 面板或者浏览器开发者工具的 Network 面板看请求状态码和响应体。一般来说出现 404 是路径写错出现 401 是 token 缺失或过期出现 500 是服务端代码异常重点看后端控制台堆栈。9. 常见问题与排查思路结合做微信小程序商城和 Vue3 管理后台的高频问题整理一份排查表问题现象可能原因排查方式解决方案小程序 wx.request 请求失败开发工具未关闭域名校验或后端未启动看 Network 面板状态码本地开发勾选“不校验合法域名”正式环境配置合法 HTTPS 域名登录后用户信息为空code 换 openid 失败检查后端日志中微信接口返回值核对小程序 AppID 和 AppSecret确保证书地址正确Vue3 中 this 为 undefined在script setup或组合式写法中误用 this看浏览器报错堆栈用 ref、reactive 和显式 import 替代 this商品下架后小程序仍能看到查询时没有过滤商品状态检查 SQL 或查询条件商品接口统一加 status 1 条件订单已支付但后台仍是待支付支付回调未正确处理或未验签查看后端日志有没有收到微信通知将支付结果更新逻辑放到回调接口二次校验签名小程序 A 跳小程序 B 不成功需要在小程序后台配置跳转权限检查微信公众平台-设置-第三方设置配置跳转小程序名单且需要同主体或关联明文 scheme 配置分包路径不行路径格式不完整核对 scheme 配置中的路径是否包含分包前缀填写完整分包路径例如packageA/pages/order/indexHBuilderX 运行微信小程序提示不是开发者微信开发者工具登录账号没有权限检查当前微信账号是否被添加为项目开发者在微信公众平台添加开发者并重新打开工具真机上顶部导航栏字体重叠自定义导航栏高度计算错误查看胶囊按钮坐标使用 wx.getMenuButtonBoundingClientRect 动态计算高度其中“小程序 A 跳小程序 B”和“明文 scheme 配置分包路径”这几类问题在功能联调后期非常常见尤其是涉及多端跳转时常常不是因为代码写错而是因为微信公众平台侧的权限配置没有到位。10. 工程层面的最佳实践与建议最后从工程角度提几条建议这些点决定项目能不能从“本地能跑”升级为“质量可用”。第一数据库设计要前置。化妆品商城至少有用户、商品、SKU、库存、购物车、订单、订单明细、支付记录、管理员等表。不要在写代码过程中频繁改表结构。使用 mybatis-plus 等工具生成基础代码后再集中精力写业务逻辑。第二接口命名要规范。小程序端接口统一/api/user/**管理后台统一/api/admin/**。写一个登录接口叫/login再写一个叫/adminLogin一段时间后自己也分不清。命名一致比代码风格一致更重要。第三不要把密钥和密码写在代码里。小程序的 AppSecret、服务端数据库密码、微信商户密钥等都应该通过配置文件或环境变量管理。SpringBoot 项目里推荐使用application.yml配合多环境配置本地用application-dev.yml生产用application-prod.yml。# 文件路径src/main/resources/application.yml server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/mall?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: ${DB_PASSWORD:root} driver-class-name: com.mysql.cj.jdbc.Driver wx: appid: ${WX_APPID:your-appid} secret: ${WX_SECRET:your-secret}第四逻辑删除代替物理删除。商品删除、分类删除建议加deleted或status字段防止误删后数据无法恢复。尤其是在运营后台一个“删除”按钮对应的是敏感操作不是万不得已不做物理删。第五全链路日志要留痕。用户下单、支付回调、管理员修改商品价格这类操作都要打日志。万一出问题第一件事是查日志而不是翻前端代码。日志中不要打印用户手机号、密码、完整微信 session_key 等敏感信息。第六上传图片走对象存储或服务器目录静态映射不要直接把图片转成 base64 存数据库。化妆品商品图片通常较大base64 存储会拖慢接口速度也会让数据库表变得难以维护。第七接口要做好异常包装。不要靠 try-catch 吞异常建议使用 Spring 的RestControllerAdvice做全局异常处理业务异常抛出明确的 message前端拿到后直接弹窗提示用户这样代码会干净很多。第八Vue3 后台做表格管理时多使用v-loading状态和空数据占位。经验不足的开发者经常忽略加载态导致用户快速点击时界面无响应。实际体验往往就差在细节上。对于做毕业设计或学习项目的同学建议记住一句经验把“注册登录-商品展示-购物车-下单-后台发货”这条主链路跑通比堆砌十个没连起来的功能模块更有价值。主链路通了SpringBoot 服务端、Vue3 管理后台和微信小程序端之间的数据交互就真正理解了。接下来再往上加优惠券、积分、秒杀、数据统计这些功能其实就是按同样的模式复制和扩展。如果时间允许可以继续研究微信支付、日志记录和接口幂等这些才是从学生项目走向企业级应用的关键分水岭。