Java + uni-app开源商城系统全栈拆解与部署实战

发布时间:2026/9/1 1:36:19
Java + uni-app开源商城系统全栈拆解与部署实战 简介这是一套面向Java后端与uniapp前端开发者的新零售电商解决方案专为快速搭建微信小程序商城而设计适用于中小型电商项目开发、毕业设计及全栈技术学习者。资源包含前后端完整源码后端基于Spring BootMyBatis实现用户管理、商品运营、订单支付等核心业务前端采用uniapp框架统一构建多端界面并深度适配微信小程序生态。压缩包共2000个文件涵盖497个Java类业务逻辑与数据访问、659个JS脚本交互与API调用、55个Vue组件可复用UI模块、260个CSS样式文件含iview、element、bootstrap等多套UI支持以及32个wxml/wxss小程序原生文件整体16.06MB结构清晰、模块解耦。已有1170人学习下载读者可直接部署运行获取从数据库设计、RESTful接口开发、小程序页面渲染到微信支付对接的全流程实践能力并参考配套JSON配置、SQL建表语句与MD文档快速上手。 如果你跟我说要做一个小程序商城第一反应往往是前端一套、后端一套、微信生态又一堆限制光是把“登录、下单、支付”这三件事跑通就够折腾好几个周末。更别提商品管理、购物车、订单状态这类看似简单、实际到处是细节的模块。所以当我看到这套 Java uni-app 的前后端全部开源商城项目时第一感受是这东西能帮你省掉大量重复造轮子的时间让你把精力放到真正重要的业务上。这个项目最大的特点就是“全”——后端用 Java 写前端用 uni-app 写小程序端可以直接跑源码全部公开。对正在学前后端分离开发的同学来说它是一份很完整的学习资料对想快速上线一个小程序商城的中小团队来说它又是一个可以拿来改改就能用的基础工程。写这篇文章我就围绕这套项目的整体设计、数据库、后端实现、前端页面、部署上线和常见坑位做一次比较完整的拆解希望对正在研究同类项目的朋友有帮助。1. 项目整体设计与技术选型——为什么看中这套 Java uni-app 组合1.1 技术栈全景与选型逻辑先列出这套项目里最核心的技术组件层级技术选型说明后端语言Java 8稳定生态成熟招聘市场上人多后端框架Spring Boot 2.x快速构建接口内置容器适合前后端分离ORMMyBatis-Plus单表 CRUD 很方便复杂 SQL 也能手写数据库MySQL 5.7最常见的关系型数据库成本低缓存Redis登录态、验证码、热点数据缓存前端框架uni-appVue 2/3一套代码生成小程序、H5、App状态管理Vuex/Pinia管理用户信息、购物车、全局状态构建工具Maven、HBuilderX后端依赖构建、前端打包发行选这套组合我自己的判断有三点。第一Java 后端做电商类系统是有历史沉淀的。订单、支付、库存这些场景Java 生态里有大量现成解决方案和开源组件遇到问题很容易搜到答案。对比 Node.js 或 GoJava 在中小团队里可能显得“重”但稳定性和可维护性确实有优势。第二uni-app 对微信小程序的支持很成熟。它本质上是用 Vue 语法写一套代码编译到不同平台。尤其微信小程序的登录、支付、分享这类能力uni-app 都封装好了虽然偶尔还是要写条件编译但总体比原生小程序开发更高效。第三前后端分离已经是当前 Web 开发的主流形态。后端只出接口前端负责页面渲染和交互。这套项目本身就是一个很好的前后端分离实战样例接口怎么设计、请求怎么封装、鉴权怎么做都能直接看到。1.2 项目目录结构与模块规划我拿到源码后首先看的就是目录结构。一个结构清晰的项目能省去很多理解成本。后端部分大致长这样mall-server ├── src/main/java/com/mall │ ├── config // 全局配置跨域、拦截器、微信参数 │ ├── controller // 接口层接收前端请求 │ ├── service // 业务逻辑层核心逻辑都在这里 │ ├── mapper // MyBatis 数据访问层 │ ├── entity // 数据库实体类 │ ├── dto // 请求参数对象 │ └── vo // 返回给前端的视图对象 ├── src/main/resources │ ├── mapper // MyBatis XML 文件 │ └── application.yml // 数据源、Redis、微信配置 └── sql └── init.sql // 数据库初始化脚本前端部分大致长这样mall-app ├── pages │ ├── index // 首页 │ ├── category // 分类页 │ ├── cart // 购物车 │ ├── user // 个人中心 │ ├── goods // 商品详情 │ ├── order // 订单确认、订单列表 │ └── address // 收货地址 ├── api // 接口请求封装 ├── utils // 工具函数 ├── static // 静态资源 ├── App.vue ├── main.js ├── manifest.json // 小程序 appid、权限配置 └── pages.json // 页面路由和 tabBar 配置这种分层比较常规但它符合大多数团队的习惯controller 只做参数接收和返回业务逻辑全部放 servicemapper 只负责数据库交互。我看过不少开源项目把业务逻辑写在 controller 里一个接口几百行改起来非常痛苦。这套项目至少在分层上没有走偏对新手来说也更容易读懂。2. 商城核心业务模块拆解与数据库设计2.1 用户端与管理端功能清单商城的核心业务无论大小基本都绕不开这些模块用户端微信登录、首页轮播、商品分类、商品列表、商品详情、购物车、下单、微信支付、订单查询、售后申请、收货地址管理、优惠券领取管理端商品上架/下架、分类管理、库存管理、订单发货、退款处理、基础数据统计这套开源项目一般会包含用户端完整功能和简单管理后台。如果管理后台没做得很完整也不用太担心因为商城最核心的链路是“浏览商品→加购物车→下单→支付→发货→收货”把这套链路跑通剩下的都是锦上添花。2.2 数据库表结构与关键字段设计我把这套项目里比较核心的几张表列一下字段不必完全照抄重点是理解设计思路用户表user字段类型说明idbigint主键openidvarchar(64)微信用户唯一标识unionidvarchar(64)用户跨应用统一标识可空nicknamevarchar(64)昵称avatarvarchar(255)头像地址phonevarchar(20)手机号可空session_keyvarchar(64)微信会话密钥敏感注意加密statustinyint状态1 正常0 禁用create_timedatetime创建时间update_timedatetime更新时间商品表goods商品表是电商系统里最核心的表之一。字段一般包括商品名称、副标题、分类 ID、主图、详情富文本、价格、库存、销量、上下架状态、逻辑删除标记。价格必须用 decimal不能用 float/double否则会有精度问题。SKU 表sku如果商品有规格比如颜色、尺码那必须有 SKU 表。一个商品对应多个 SKU每个 SKU 有自己的价格和库存。有些简单的商城项目没有 SKU 表商品直接一个价格一个库存但真正要商用SKU 是绕不开的。订单表order字段类型说明order_novarchar(32)订单号唯一索引user_idbigint下单用户total_amountdecimal(10,2)订单总金额pay_amountdecimal(10,2)实付金额pay_statustinyint支付状态0 未支付1 已支付order_statustinyint订单状态待付款、待发货、待收货、已完成、已关闭address_snapshotvarchar(500)收货地址快照 JSONpay_timedatetime支付时间create_timedatetime下单时间订单明细表order_item订单明细记录每个订单里的具体商品。关键点是商品名称、价格、图片、数量都要冗余存储。为什么因为商品信息后续可能会改甚至可能被删除但订单里的快照必须保留否则用户查历史订单时看到的商品信息会变。购物车表cart购物车表相对简单用户 ID、商品 ID、SKU ID、数量、选中状态。但要注意一点购物车数据可以存数据库也可以存 Redis。存数据库实现简单但频繁读写压力大存 Redis 性能好但需要考虑持久化和数据一致性问题。这套项目如果用了 Redis把购物车放 Redis 也是一个合理方案。2.3 数据库设计里容易被忽略的几个关键点第一订单状态和支付状态要分开。很多新手会把这两个字段合成一个比如“已支付”就算“已发货”结果后续退款、售后的时候状态根本不够用。分开之后订单状态管履约流程支付状态管资金流逻辑清晰很多。第二金额运算全部用分为单位或 decimal。与钱相关的计算前端展示用元后端计算用分避免浮点误差。第三库存扣减要用条件更新。比如下单时执行update goods set stock stock - #{num}, sales sales #{num} where id #{id} and stock #{num}这样利用数据库行锁保证不会超卖。如果先查库存、再判断、再更新并发场景下肯定会出问题。3. 后端 Spring Boot 实现要点与代码还原3.1 微信小程序登录与 token 体系商城第一个要解决的问题就是“用户是谁”。微信小程序提供了 wx.login 拿到临时 code后端拿 code 去微信接口换 openid 和 session_key然后确定用户身份。整个流程是前端调用uni.login拿到 code前端把 code 传给后端/api/auth/login后端用 code 请求微信jscode2session接口微信返回 openid 和 session_key后端查数据库用户不存在则自动注册后端生成 token存 Redis返回给前端前端把 token 缓存起来后续请求都带上我见过不少项目把 openid 直接返回给前端这是不合适的。openid 是用户在小程序里的唯一标识相当于用户的身份证号前端拿到它没有意义反而增加了泄露风险。正确做法是后端生成一个随机的 token 作为登录凭证openid 只保存在服务端。核心代码大致是这个意思PostMapping(/auth/login) public Result login(RequestBody LoginDTO dto) { // 1. 调用微信接口换取 openid String url https://api.weixin.qq.com/sns/jscode2session ?appid appid secret secret js_code dto.getCode() grant_typeauthorization_code; WechatSession session restTemplate.getForObject(url, WechatSession.class); // 2. 根据 openid 查找用户不存在则注册 User user userMapper.selectByOpenid(session.getOpenid()); if (user null) { user new User(); user.setOpenid(session.getOpenid()); userMapper.insert(user); } // 3. 生成 token 存入 Redis设置过期时间 String token UUID.randomUUID().toString().replace(-, ); redisTemplate.opsForValue().set(token: token, user.getId(), 7, TimeUnit.DAYS); return Result.ok(token); }拦截器里要校验 tokenpublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String token request.getHeader(token); if (StringUtils.isBlank(token)) { throw new BusinessException(未登录); } Object userId redisTemplate.opsForValue().get(token: token); if (userId null) { throw new BusinessException(登录已过期); } request.setAttribute(userId, userId); return true; }这里有个很容易踩的坑Redis 里存的 key 如果没用前缀区分业务不同模块之间可能互相覆盖。所以 token 的 key 一定要加上业务前缀比如token:xxxx。3.2 商品列表与购物车接口设计商品模块的接口一般包括接口方法说明/api/goodsGET分页查询商品支持分类筛选、关键字搜索/api/goods/{id}GET商品详情/api/cart/listGET购物车列表/api/cart/addPOST加入购物车/api/cart/updatePUT修改购物车商品数量/api/cart/checkedPUT修改选中状态/api/cart/deleteDELETE删除购物车商品接口设计上要注意列表接口不要一把梭把所有字段查出来要分页。详情接口要包含富文本、轮播图、SKU 列表等完整信息但用不着把浏览量、点击量这些非核心字段全塞进去。购物车里“选中状态”这个字段容易被人忽略。很多商城购物车是支持勾选一部分商品去结算的所以购物车表里要有 checked 字段。后端在下单时只处理选中状态的商品。如果把这个逻辑放到前端处理用户切换设备后数据就不一致了。3.3 订单生成与微信支付对接订单是整个商城里最复杂的链路也最容易出问题。我把流程拆开写创建订单接收请求从购物车中读取选中的商品或者直接传商品 ID 和数量校验商品是否存在、是否上架、库存是否充足生成唯一订单号计算订单总金额插入订单表和订单明细表扣减库存清空对应购物车数据订单号生成有一些讲究。最简单的方案是用时间和随机数拼接但要注意并发重复。更稳妥的方式是日期 用户 ID 随机数或者直接用 Redis 自增序列。订单号要有唯一索引防止重复。微信支付微信支付流程对第一次接触的同学来说有点绕。我来理清一下。第一步后端调用微信支付“统一下单”接口传入业务参数订单号、金额单位是分、用户 openid、支付回调地址。微信返回一个 prepay_id。第二步后端拿着 prepay_id 和时间戳、随机字符串、签名一起返回给前端。第三步前端用uni.requestPayment调起微信支付面板。第四步用户输入密码完成支付微信服务器异步通知后端的回调地址。第五步后端收到回调先验签再校验订单号和金额最后更新订单状态为已支付。回调处理是重中之重。微信支付回调可能因为网络原因重复发送所以服务端处理必须幂等。也就是说同一个订单收到多次回调最终结果必须一样不能因为回调两次就把订单状态覆盖成“已发货”或者重复加积分。我在项目里见过一个很低级的错误回调里直接把订单状态改成“已支付”但没判断订单当前状态。结果用户在未支付状态下点了取消然后微信回调到了订单状态又变成了已支付最后用户的订单要人工介入才能处理。正确做法是只有在订单原状态是“待付款”时才更新为“已支付”其他情况直接忽略。回调接口代码大致是这样PostMapping(/api/pay/notify) public String payNotify(HttpServletRequest request) { // 1. 读取回调数据 String body StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8); // 2. 解密验签确认是微信发的 MapString, String params WxPayUtil.decrypt(body); if (!WxPayUtil.verifySign(params)) { return fail; } // 3. 取出订单号和金额 String orderNo params.get(out_trade_no); String totalFee params.get(total_fee); // 4. 查订单校验金额 Order order orderMapper.selectByOrderNo(orderNo); if (order null || !order.getPayAmount().equals(new BigDecimal(totalFee).divide(new BigDecimal(100)))) { return fail; } // 5. 幂等更新 int rows orderMapper.updatePayStatus(orderNo, 1, 0); if (rows 0) { return success; } // 6. 其他业务通知发货、加积分等 return success; }这里要注意回调接口返回给微信的必须是success或fail字符串不能返回 JSON。微信只有收到success才认为通知成功否则会持续重试。3.4 并发场景下的库存扣减如果你只做演示库存扣减随便写写没关系。但真实商城超卖是必须解决的问题。最常见的做法有两个第一种是数据库乐观锁。商品表加一个 version 字段更新时带上 versionversion 不匹配就更新失败。但这种方案在库存扣减这种场景下显得绕而且重试逻辑要自己写。第二种是条件更新也就是我前面写的where stock #{num}。利用数据库行锁同一时刻只有一个事务能更新成功天然防超卖。大多数中小商城用这种方式足够了。如果你的并发量真的很高再考虑 Redis 预扣库存但这套方案的复杂度会大幅提升普通项目没必要。4. 前端 uniapp 页面实现与微信小程序适配4.1 页面结构、tabBar 与分包加载前端页面结构里最常见的四个 tab 是首页、分类、购物车、我的。在pages.json里配置 tabBar对应这四套页面。商城应用不是只有这几个页面。商品详情、订单确认、订单列表、支付结果页、地址管理、售后页这些页面都属于低频访问页面。在微信小程序里主包体积限制是 2M超了就得用分包。所以页面规划时可以把高频页面放在主包低频页面放分包。分包有两点要注意第一分包不能嵌套只能是主包和一级分包这种结构。第二页面跳转时如果目标页面在分包里用uni.navigateTo传入分包路径即可不需要额外配置。但如果要访问分包里的组件那就涉及“分包异步化”的问题需要配置。对于商城来说如果商品图片不多、代码也不复杂第一阶段不分包也能过但建议从一开始就做好目录规划避免后期迁移成本。4.2 请求封装与登录态处理前后端分离的核心就是接口调用。uni-app 里用uni.request发起请求但它是个回调函数如果不做封装页面里会写一堆重复代码。我自己习惯把请求封装成一个 Promise 方法统一处理 baseURL、token、错误码、加载动画// utils/request.js const BASE_URL https://api.example.com export function request(options) { return new Promise((resolve, reject) { uni.request({ url: BASE_URL options.url, method: options.method || GET, data: options.data || {}, header: { token: uni.getStorageSync(token) || }, success: (res) { if (res.data.code 0) { resolve(res.data.data) } else if (res.data.code 401) { // token 过期重新登录 uni.navigateTo({ url: /pages/login/login }) reject(res.data) } else { uni.showToast({ title: res.data.msg, icon: none }) reject(res.data) } }, fail: (err) { uni.showToast({ title: 网络异常, icon: none }) reject(err) } }) }) }关于登录态有一个很常见的坑小程序里wx.login生成的 code 是一次性的并且有效期很短前端不能把 code 存起来备用。每次登录都必须在需要时重新调用。另外要区分两个“登录”概念一个叫静默登录。用户进入小程序后端自动用 code 换 openid 创建用户这个过程用户无感知。用户进来就已经是登录状态了。另一个叫主动授权登录。用户点击“微信登录”按钮前端调用授权弹窗拿到用户头像昵称再调后端接口更新用户资料。几乎所有商城都建议先做静默登录不然用户一进来就看到一堆需要登录的提示体验非常差。4.3 首页、商品详情、购物车与订单页实现首页通常由搜索框、轮播图、金刚区图标、商品推荐列表组成。数据来源是后端接口轮播图接口返回 banner 列表商品接口返回推荐商品。首页不需要太复杂重点是首屏加载速度。图片要压缩列表要分页不能一次查几千条数据。商品详情页核心数据包括商品信息、轮播图、价格、销量、库存、SKU 规格选择、图文详情。SKU 选择的逻辑稍微麻烦一点用户选了某个规格要计算哪些 SKU 是可选的哪些是缺货下架的。简单做法是遍历所有 SKU匹配用户当前已选规格能匹配的显示可点击否则置灰。购物车页除了基本列表还要处理全选、单选、数量加减、左滑删除。购物车底部的“合计金额”要在前端实时计算但真正下单时以后端计算为准。前端可以先给用户一个预估后端做最终校验。订单确认页用户从购物车或商品详情页跳过来展示收货地址、商品清单、运费、总金额点击“提交订单”后调后端创建订单接口成功后调起支付。支付代码比较简单uni.requestPayment({ provider: wxpay, timeStamp: res.timeStamp, nonceStr: res.nonceStr, package: res.package, signType: MD5, paySign: res.paySign, success: () { uni.redirectTo({ url: /pages/order/paySuccess }) }, fail: (err) { // 用户取消支付跳到订单列表让用户重新支付 } })有个细节要注意package参数的值一定是prepay_idxxx这个完整格式很多新手只传了prepay_id导致调起支付失败。4.4 微信小程序特有的适配坑微信小程序虽然上手简单但适配问题不少。rpx 和 px 的换算小程序里 750rpx 等于屏幕宽度开发时不用担心不同机型。但如果页面里嵌入了第三方 H5 或者原生组件尺寸单位就还得自己处理。安全区适配iPhone X 以后的机型底部有 home 条页面底部按钮会被遮挡。处理方式是在底部容器加 paddingpadding-bottom: constant(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom);图片域名必须配白名单小程序里image标签加载的图片域名必须在小程序后台配置为“downloadFile 合法域名”而且必须 HTTPS。开发模式下可以在“详情→本地设置”里勾选“不校验合法域名”但上线前一定要配置好。头像昵称填写规则现在微信不再支持wx.getUserInfo直接弹窗拿头像昵称而是推荐用button的open-typechooseAvatar和input的typenickname让用户主动填写。如果你还在用老方法审核大概率会挂。隐私协议弹窗微信对用户隐私的管控越来越严小程序中用到的隐私接口要先声明。到时间节点后强制要求隐私弹窗授权商城这种收集用户昵称头像的手机号的项目一定要提前把隐私政策页面做好。5. 本地搭建、部署上线与二次开发实操指南5.1 从拉取源码到本地跑通前后端我按照实际流程走一遍告诉你怎么在本地把项目跑起来。第一步准备环境安装 JDK 1.8、Maven、MySQL 5.7、Redis、HBuilderX、微信开发者工具。第二步导入数据库。项目里一般会有sql/init.sql或者doc/init.sql在 MySQL 里执行创建数据库和表。如果提供了初始化数据那就更好首页不至于一片空白。第三步修改后端配置。打开application.yml把数据源地址、账号、密码改成你自己的Redis 地址也改一下。如果有微信相关配置先填上测试用的 appid 和 secret没有的话登录功能暂时不可用但其他接口能测。第四步启动后端。在项目根目录执行mvn spring-boot:run后端起来之后先访问一下接口文档比如http://localhost:8080/api/goods能返回 JSON 就说明环境没问题。第五步用 HBuilderX 导入前端项目。在manifest.json里配置小程序 appid然后运行到微信开发者工具。第六步微信开发者工具打开后点击“详情→本地设置”勾选“不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书”。这是因为本地调试用的通常是 http 地址微信默认禁止。到此前后端就打通了。5.2 前后端分离联调与接口自测前端开发中用的接口地址和后端本地地址不一样怎么解决两种方式。第一种在request.js里把 baseURL 拆成多个环境变量const ENV dev const BASE_URL { dev: http://localhost:8080, prod: https://api.example.com }[ENV]第二种用 HBuilderX 里常见的方式开发环境请求走本地局域网 IP比如http://192.168.1.100:8080真机调试时手机和电脑在同一 Wi-Fi 下就能访问。接口自测推荐用 Apifox 或 Postman。先把后端接口导出来所有接口测通过之后再联调前端能省非常多时间。5.3 生产环境部署本地跑通只是第一步上线才是真正的考验。上线流程大概是后端打包mvn clean package -DskipTests生成 jar 包后上传到服务器用 Java 命令启动。更推荐的方式是写一个 Dockerfile 或直接用 docker-compose把 MySQL、Redis、后端服务一起编排起来。一个小型商城用 docker-compose 管理三个容器就够了version: 3 services: mysql: image: mysql:5.7 restart: always environment: MYSQL_ROOT_PASSWORD: yourpassword MYSQL_DATABASE: mall volumes: - ./sql:/docker-entrypoint-initdb.d redis: image: redis:6 restart: always server: build: . restart: always ports: - 8080:8080 depends_on: - mysql - redisNginx 负责反向代理和 HTTPS 配置。小程序要求所有请求域名必须是 HTTPS所以你需要一个已备案的域名并配置 SSL 证书。Nginx 配置里把/api的请求转发到内网的 8080 端口。前端打包在 HBuilderX 里选择“发行→小程序-微信”生成微信小程序代码包在微信开发者工具里打开点击“上传”然后到微信公众平台提交审核。还有图片存储的问题。本地存储图片在联调时没问题但生产环境建议用对象存储服务比如阿里云 OSS、腾讯云 COS或者七牛云。商品图片、用户头像都放到对象存储后端只存 URL。否则服务器磁盘会很快被打满而且图片加载速度也会拖慢用户体验。5.4 基于开源项目的二次开发建议拿到开源项目后怎么改才能不踩坑我的经验是先读懂再动手。第一步把“用户登录→浏览商品→加购物车→下单→支付”这条主链路完整跑通边跑边打断点搞清楚每次请求经过哪些类、哪些方法。第二步不要上来就重构。开源项目的代码风格可能不符合你的习惯但它能跑。等你完全理解每一行代码为什么这么写之后再决定要不要改。第三步从一个小需求开始实践。比如给商品列表加一个“新品”标签或者给订单列表加一个“取消订单”按钮。通过改一个小功能你能快速熟悉项目的代码习惯和扩展方式。常见的扩展方向优惠券模块后台发券、用户领券、下单抵扣秒杀模块Redis 预扣库存 限流 异步下单分销模块用户推广关系绑定、分销佣金结算多商户支持把商品表加一个商户 ID订单结算时拆单这些都是很成熟的场景网上也有大量参考实现。6. 常见问题与排坑记录6.1 真机预览时请求失败表现开发者工具里页面正常手机预览时首页空白、数据加载不出来。原因基本是这么几个前端请求的 baseURL 是http://localhost手机访问的是电脑的 localhost当然是通的。要改成局域网 IP。后端没监听0.0.0.0只监听了本地回环地址局域网访问不到。Spring Boot 默认是0.0.0.0一般不会出这个问题但如果你用了自定义配置确认一下。手机和电脑不在同一个网段防火墙拦截了端口。排查方法先用手机浏览器访问一下http://电脑IP:8080/api/goods能通说明后端没问题问题在前端配置。6.2 支付回调不触发或重复触发回调不触发去查这么几项回调地址是不是公网可访问本地开发没有公网地址微信服务器根本调不到。可以先用内网穿透工具暴露本地端口。回调地址的路径和后端代码是否完全一致回调地址是不是 HTTPS微信支付回调要求 HTTPS本地调试时可以临时用 HTTP 但生产必须 HTTPS。回调重复触发是正常现象微信会重试直到收到success。所以回调逻辑必须幂等。签名验证失败先检查密钥是否配置正确再检查参数拼接顺序是否和微信文档一致。不同语言的签名实现细节不同Java 里尤其要注意空值和大小写。6.3 登录态突然失效用户操作到一半被弹回登录页体验非常糟糕。常见原因token 过期时间太短。如果只是个小商城token 有效期设 7 天比较合适。Redis 数据丢失。Redis 如果没有持久化重启后所有 token 都没了用户全部掉线。生产环境要开启 AOF 或 RDB 持久化。前后端时间不一致。如果用了 JWT签名验证时需要对比时间服务器和客户端时间偏差大会导致 token 提前失效。6.4 小程序提审被拒小程序审核被拒最常见的几条原因类目选择不对。商城类小程序需要选择“电商平台”或“商家自营”类目并上传相应资质。涉及虚拟商品支付。微信对虚拟支付管控很严如果商城卖的是会员、课程、虚拟币这类虚拟商品个人主体基本过不了。解决方案是走微信的小程序虚拟支付能力或者做成 H5 支付。没有隐私政策。小程序里收集用户信息必须有明确的隐私政策弹窗说明收集了哪些信息、用来做什么。需要测试账号。如果管理后台登录需要账号密码提审时要在后台配置体验账号否则审核员进不去。6.5 常见问题速查表问题原因解决思路真机加载不出图片图片域名没有加入 downloadFile 白名单或不是 HTTPS后台配置白名单图片改成 HTTPSrequestPayment 提示参数错误package 参数少了prepay_id前缀检查参数拼装格式下单提示库存不足库存扣减逻辑写成了先查后改改成条件更新 SQL回调后订单还是待付款回调里没做幂等处理订单状态被后续请求覆盖加状态判断只有待付款才更新购物车数据丢失购物车只存在 localStorage用户换设备就没了购物车数据同步到后端支付金额少了 0.01前后端金额单位不统一统一用分传给微信支付展示时转元页面跳转后数据没刷新页面 onLoad 只执行一次再次进入时走了缓存在 onShow 里重新拉取数据6.6 我的几点避坑经验最后说几个我在实际开发中悟出来的经验不算高深但很实用。第一商城项目里订单状态机的设计一定要想清楚再写代码。把“待付款→待发货→待收货→已完成”这条主链路和“已取消、售后中、已退款”这些分支画清楚后面加功能、改 bug 都会轻松很多。我见过太多项目因为状态机混乱改一个状态影响一片逻辑。第二日志必须打足。尤其是支付回调、订单状态变更这种关键节点每一步都要有日志。出问题的时候没有日志你会非常痛苦。第三不要把开源项目的代码当成完美的。开源项目解决的是通用问题很多细节是参差不齐的。你在二次开发时一定要自己把“登录→下单→支付”这条链路的关键逻辑完整读一遍确认没有明显漏洞再上线。尤其是涉及钱的逻辑怎么小心都不为过。这套 Java uni-app 的开源商城项目像一套可以正常运转的样板间。你可以直接入住也可以照着它的格局重新装修。最怕的是拿到源码之后一上来就这里改改那里删删结果改了三天发现项目跑不起来了。从读懂到改好中间没有捷径但沿着“主链路走通→读关键代码→小改动试水→完整功能开发”的路线收益会是最稳的。本文还有配套的精品资源点击获取