SpringBoot+uniapp商城项目实战:从选型到部署的完整踩坑指南
简介这是一份基于SpringBoot与uniapp的商城项目源码包面向具有一定Java基础、想系统练习全栈或跨端开发的学习者。项目后端以SpringBoot为主前端采用uniapp整体参考linjiashop开源商城设计覆盖用户注册登录、商品分类展示、购物车、订单管理等典型电商流程并包含商品查询、提交订单等接口示例。资源共67个文件压缩包仅64KB其中60个java文件构成业务核心体现控制器、服务层与数据访问层的分层实现3个xml和2个yml用于MyBatis/JPA映射与端口等环境配置1个md说明文档便于快速了解项目结构另有.gitignore辅助版本管理。目前已有1938人浏览学习。通过学习源码可掌握RESTful API注解开发、uniapp跨端页面与请求封装、前后端JSON交互、数据库字段设计及基础安全与性能优化思路适合作为商城类全栈项目的入门参考和二次开发模板。 去年团队接了一个商城项目的开发任务产品经理扔过来一句话小程序、App、H5 一个都不能少后台管理再补一套 PC 端。这句话基本就给技术选型定了调。最终我们选择的后端是 SpringBoot前端是 uniapp项目代号就叫 shop。整套系统从订单流程到多端打包发布都踩了一遍坑这篇把其中真正值得参考的细节写出来给同样想用 SpringBoot uniapp 做商城项目的朋友一个完整参考。1. 商城项目为什么选 SpringBoot uniapp一次多端交付的现实决策1.1 技术选型的核心逻辑谁在什么时候用什么端很多人讨论技术选型爱从哪个框架更先进出发但我建议先看交付面。商城类项目有两个典型角色C 端消费者逛商品、下单、查订单用的设备极其分散有微信小程序、有安卓 App、有苹果 App、还有人直接开手机浏览器访问 H5B 端运营和客服基本只坐在电脑前用 PC 后台管理商品、订单、用户数据。如果 C 端四个平台各写一套原生代码一个前端小组根本交付不过来。uniapp 的核心价值就在这里一套 Vue 语法代码编译到小程序、App、H5 三端覆盖了商城项目绝大部分终端场景。而后台管理这种强交互、重表格、重权限的系统单独用 Vue 或 React 写 PC 端是更稳的做法没必要硬塞进 uniapp。SpringBoot 作为后端也同样是随便招一个 Java 开发就能上手的务实选择生态成熟、集成第三方支付和物流接口的资料多、团队踩坑成本低。一个商城项目最怕的不是技术不够新而是问题没人解决过。1.2 为什么不选 Flutter 或 React Native聊选型绕不开 Flutter 和 React Native。我在项目立项时专门做了一次对比最终放弃它们不是因为技术差而是跨端范围不匹配对比项uniappFlutterReact Native小程序支持原生支持直接编译需额外套一个编译层维护成本高需额外方案成熟度一般H5 支持支持较完善支持较弱常用于 Web 实验支持一般App 性能接近原生复杂动画吃亏自绘引擎性能强原生组件桥接性能较好前端上手成本Vue 语法门槛低需要学 Dart需要学 React 与 RN 生态社区与招聘国内电商类项目多生态热度高热度高但偏移动端热度下降招人难度上升商城项目不是重动画、重计算的 App性能瓶颈主要在接口和图片加载上。用 uniapp 换来的开发效率和全端覆盖远比那一点性能差距值钱。当然如果你的核心场景是大规模列表滚动、复杂手势交互Flutter 确实更有优势但那是另一个故事了。1.3 项目模块怎么划分后端我用了 SpringBoot 多模块结构避免所有代码堆在一个工程里shop ├── shop-common // 公共工具类、统一返回结果、异常码 ├── shop-framework // 配置、安全、拦截器、文件存储 ├── shop-api // C 端接口商品、购物车、订单、支付 ├── shop-admin // B 端接口商品管理、订单管理、用户管理 └── shop-job // 定时任务超时关单、库存回补前端 uniapp 这边按业务分包商城核心页面放主包活动页、售后流程这类低频页面用分包加载能显著缩小首包体积这对小程序的审核和首屏速度都有帮助。这个划分方式在项目中期被验证是对的C 端和 B 端接口权限模型完全不同拆开之后互相不干扰单独发布迭代也方便。2. SpringBoot 后端落地版本矩阵、自动装配与订单核心链路2.1 先把版本矩阵定死SpringBoot 2.7 还是 3.x 不只是一个选择题搜索热度里springboot版本太高出现得很多这背后是真实的团队困境。SpringBoot 3.x 要求 JDK 17而很多商城项目团队现有的基础设施、运维脚本、依赖包还停留在 JDK 8 生态。如果贸然升级第一个撞上的就是包名变更javax.servlet变成了jakarta.servlet。我的一个朋友从 2.7 升 3.x 时光是把代码里import javax.annotation.*全部替换成jakarta.annotation.*就花了一天后来还发现某个支付对接的第三方 SDK 根本不支持 JDK 17。我给商城项目的版本矩阵建议是properties java.version1.8/java.version spring-boot.version2.7.18/spring-boot.version mybatis-plus.version3.5.3.2/mybatis-plus.version hutool.version5.8.25/hutool.version jjwt.version0.11.5/jjwt.version /propertiesSpringBoot 2.7 是 2.x 的最终版本官方维护周期长社区资料最全JDK 8 环境下非常稳。如果你是新项目、团队又全员熟悉 JDK 17那直接上 3.2 没问题但前提是第三方依赖都确认过兼容性尤其是支付、短信、对象存储这类商城绕不开的中间件 SDK。另外补充一点spring-boot-maven-plugin在做镜像构建时3.x 的build-image功能更完善但那是部署环节的事别因为想用这个功能就把整个后端架构推倒重来。2.2 自动装配原理为什么引入依赖就能用不生效时又该查哪里很多做商城项目的开发被面试官问SpringBoot 自动装配原理时能说个大概但实际排错时就抓瞎。简单说SpringBootApplication注解里有个EnableAutoConfiguration它在启动时会读取 META-INF 下的自动装配文件SpringBoot 2.7 及之前读取的是spring.factoriesSpringBoot 3.x 改成了AutoConfiguration.imports文件里列了一堆配置类每个配置类通常配合ConditionalOnClass、ConditionalOnProperty、ConditionalOnMissingBean等条件注解生效。举例来说你在 pom 里引入spring-boot-starter-data-redis自动配置类检测到 classpath 里有 RedisTemplate 相关类就会自动创建连接工厂和模板对象。所以当你的 Redis 配置不生效时第一反应不应该是盲目写Bean而是去检查classpath 是否真的引入了对应 starter配置文件中是否有关键开关被误关是否自己定义了同名 Bean 覆盖了默认的自动配置我在一个订单模块里就踩过这个坑自定义了一个RedisTemplate但没有指定序列化器结果把LocalDateTime直接存进去反序列化时全部报错。当时一直怀疑是版本问题最后翻源码才发现是自动配置被自定义 Bean 覆盖了。建议在项目初期就把常用自动配置类的源码打开看一眼比出了问题再找效率高很多。2.3 订单状态机、库存扣减与超时关单商城项目与普通 CRUD 项目最大的区别在订单链路。一个订单从创建到完成通常经历待支付、已支付、待发货、已发货、已完成、已关闭、售后中。这些状态之间不是随便跳的比如已发货不能直接变成待支付已关闭也不能变成已完成。我习惯在代码里用一个枚举加一个状态流转表来管理而不是到处写魔法数字public enum OrderStatus { PENDING_PAYMENT(0, 待支付), PAID(1, 已支付), SHIPPED(2, 已发货), COMPLETED(3, 已完成), CLOSED(4, 已关闭); private final int code; private final String desc; }库存扣减是另一个重灾区。简单做法是在下单时直接UPDATE product_sku SET stock stock - #{count} WHERE id #{skuId} AND stock #{count}靠数据库行锁保证不会超卖。但秒杀、高并发场景下这个方案会把数据库打爆。更常见的方案是先把库存预热到 Redis用 Lua 脚本原子扣减再异步同步回数据库。商城项目如果预期流量不大数据库扣减足够如果要做秒杀Redis 预热加异步落库才是比较稳的路径。订单超时未支付需要自动关闭这也是商城必备功能。我们盯上了定时任务——用 PowerJob 配置了一个每日扫描任务每过 30 秒扫一次待支付订单超过 30 分钟没支付的自动关单并把预占库存释放。用定时任务要注意幂等任务重复执行也不能影响订单状态。2.4 最容易漏的三项配置上传、资源映射、日期序列化商城项目绕不开图片上传、商品详情富文本里的图片访问、订单时间的格式化。这三个配置看着基础但一旦漏了联调阶段就是连环炸。大文件上传需要先调大限制默认只有 1MB 左右肯定不够用spring: servlet: multipart: max-file-size: 50MB max-request-size: 100MB图片传到本地磁盘后用户访问不到因为 SpringBoot 默认不映射任意目录。需要加一个资源映射Configuration public class WebMvcConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceHandler(file: uploadPath /); } }日期格式问题更隐蔽。后端返回LocalDateTime时Jackson 默认序列化出来是2024-05-20T10:15:30前端小程序端显示起来特别别扭。我在配置里统一处理掉spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8还有一点容易忽略SpringBoot 2.6 开始默认禁止循环依赖如果项目里出现A - B - A这种依赖启动时直接报错。网上很多老教程建议在配置里设置spring.main.allow-circular-referencestrue强行放行我的态度是新代码一律通过构造器注入加合理拆分解决不改配置绕过不然以后根本不敢动那两个类。2.5 单元测试别只测 Service接口层的测试价值更大商城项目涉及金额计算、库存变化出 bug 的代价比普通系统高。接手项目之初我就定了一条规矩每个订单接口都要有单元测试。但注意SpringBoot 的单元测试最佳实战不是拿SpringBootTest把整个容器拉起来再跑那样速度慢且依赖环境。正确的姿势是纯业务逻辑价格计算、状态流转用 JUnit 5 Mockito不启动容器涉及数据库操作的用SpringBootTest加 H2 内存库或 Testcontainers涉及第三方接口的用 MockWebServer 模拟我见过太多团队单元测试写成了会亮的绿灯测试代码把数据库连着、Redis 连着跑一次要两分钟最后 CI 上总是超时大家就约定俗成不跑了。这种测试不如不写。把测试控制成秒级开发才会愿意在每次改动后跑一遍。3. uniapp 端最容易翻车的点登录态、滚动冲突、视频播放与分享3.1 manifest.json 是打包前的第一道关卡uniapp 项目的manifest.json作用比很多人以为的大得多。它不只是个应用信息配置文件还直接决定你打包出来的 App 能调用哪些原生模块。商城项目里常见的几个场景要用地图选收货地址必须勾选 Maps 模块要用扫码功能必须勾选 Barcode二维码扫码模块要用消息推送必须勾选 Push 模块并配置厂商通道要使用蓝牙连接打印机必须勾选 Bluetooth 模块这些模块在 HBuilderX 云打包时会写进原生工程。如果没勾选代码里调用uni.chooseLocation或uni.scanCode时App 端要么直接报错要么没反应小程序端却一切正常。这种开发时好好的打包后失效的问题九成出在 manifest 配置上。另外H5 端跨域问题也需要额外注意开发时用 devServer 配置 proxy生产环境则靠后端网关解决。3.2 多端登录态的差异处理商城 C 端的登录链路比想象中分裂微信小程序走uni.login获取 code由后端调微信接口换 openidApp 端可能是手机号一键登录H5 端则可能是账密登录。三端不能共用同一套前端逻辑但后端返回的登录凭证可以统一成 token。前端这层我用了一个简单的请求拦截器// request.js const request (options) { return new Promise((resolve, reject) { const token uni.getStorageSync(token) uni.request({ url: BASE_URL options.url, data: options.data, header: { Authorization: Bearer token }, success: (res) { if (res.data.code 401) { uni.removeStorageSync(token) uni.navigateTo({ url: /pages/login/login }) } else { resolve(res.data) } }, fail: reject }) }) }这里的 401 统一处理要注意一点商城页面跳转链路可能很深用户在订单确认页登录超时被弹回登录页登录成功后最好有一个回跳逻辑否则用户得重新一步步点进来流失率很高。我在项目中用了一个全局变量记录登录前页面路径登录成功后再重新跳转。3.3 下拉刷新与页面滚动的冲突问题根源与解法uniapp 下拉如何触动滚动屏而不触发页面下拉刷新这个问题被搜了很多次说明它确实困扰了大量开发者。问题场景是页面里有一个scroll-view纵向滚动你期望列表滚动到顶部后继续下拉触发列表自己的刷新逻辑而不是把整个页面拽下来触发onPullDownRefresh。冲突的根源在于页面级下拉刷新和scroll-view滚动是两套手势系统。默认情况下scroll-view滚动到顶后继续下拉手势会被页面层接管。我试验后比较稳的方案是页面配置文件里关掉enablePullDownRefresh在scroll-view上监听scrolltoupper事件配合自己写的下拉刷新动画组件。这样刷新逻辑完全收敛在列表组件内部不跟页面手势打架。代价是要自己实现刷新态和动画但体验是可控的。另外注意scroll-view需要设置固定高度或flex: 1否则列表不会出现滚动条事件也就不会触发。3.4 renderjs 播放 mp4 失败问题大概率出在编码项目里有一个商品详情视频用户手机拍摄上传后在 App 端能播放但在 H5 端怎么都放不出来。一开始怀疑是 renderjs 的兼容问题花了不少时间排查最后发现根因是视频编码格式手机录制的 mp4 很多是 HEVCH.265编码而大部分浏览器只支持 AVCH.264编码。renderjs 解决的是视图层与逻辑层通信的问题它没法把一个浏览器解不出来的编码格式变出来。解决办法是在上传视频链路里增加一道转码服务用 FFmpeg 把上传视频统一转成 H.264 AAC 编码的 mp4ffmpeg -i input.mp4 -c:v libx264 -profile:v main -crf 23 -c:a aac output.mp4如果是商城项目建议在服务端上传接口就做转码而不是把压力丢到前端。这个坑属于典型的非 uniapp 的问题但 uniapp 项目一定会遇到排查的时候先确认编码再谈其他。3.5 自定义分享微信小程序和 App 的逻辑完全不同uniapp 自定义分享好友是商城运营的刚需但三端实现方式差异很大。微信小程序在页面里写onShareAppMessage和onShareTimeline可以自定义标题、图片和路径App 端则要调用plus.share或者使用 uni 的分享组件配置微信 SDK、QQ SDK 等H5 端最简单本质是复制链接。我踩过的坑是在小程序端自定义分享卡片时imageUrl必须是小程序白名单域名下的 HTTPS 图片且分享路径path需要带参数。如果imageUrl填的是本地相对路径分享卡片会显示空白。当时为了这个问题最后把所有分享图统一放到 CDN 域名下才彻底解决。线上商城如果有拼团、砍价这类裂变玩法分享参数的正确性直接决定活动效果值得早点设计好。4. 从开发机到应用市场打包、上架与 Docker 部署全链路4.1 HBuilderX 云打包与离线打包的取舍uniapp 打包 App 有两种方式HBuilderX 云打包和离线打包。云打包的优势是省事界面点点就出 apk 或 ipa缺点是受平台网络影响而且自定义原生插件的空间有限。离线打包则需要下载官方 SDK、用 Android Studio 自己组装工程适合需要集成原生 SDK 或做深度定制的项目。离线打包最大的坑是 SDK 版本必须和 HBuilderX 版本严格对应。有一次我们 HBuilderX 更新后发现 App 一打开就闪退查了很久才发现是离线打包 SDK 还在用旧版本和新版运行时不一致。这个问题的排查链路是先看崩溃日志中是否有UniSDK相关异常再对比 HBuilderX 版本和 SDK 版本。官方文档里有一个版本对应表开发前第一件事就是核对它。另外离线打包的 apk 在 Android 13 以上设备上如果没做相关权限适配打开相册、定位都会异常manifest 里的权限声明要同步处理。4.2 安卓应用市场上架与 iOS TestFlight应用市场审核是商城项目逃不掉的痛苦环节。安卓主流市场要求的材料包括软件著作权证书、隐私政策、应用备案信息。隐私政策尤其关键小程序端要求收集用户信息前必须弹窗说明App 端上架时也必须在应用内展示隐私政策涉及读取通讯录、定位、相册的权限还需要逐项说明用途。iOS 端上架前测试用的是 TestFlight流程相对固定在 App Store Connect 上传构建版本添加测试员然后通过 TestFlight 安装测试。商城的支付功能只能用实物购买不能用虚拟商品支付这一点在审核时很容易被卡如果是虚拟商品项目要提前想好替代方案。4.3 SpringBoot 打包到 DockerJDK 版本必须前后一致搜索词里的springboot jdk1.8打包到 docker desktop说明很多人在这块栽过跟头。SpringBoot 项目用 JDK 8 编译Docker 基础镜像却用了openjdk:17-jre启动时直接报UnsupportedClassVersionError。这是一个很无语但非常常见的错误。商城后端我用的 Dockerfile 长这样FROM openjdk:8-jdk-alpine LABEL maintainershop-team ENV TZAsia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime echo $TZ /etc/timezone VOLUME /tmp COPY target/shop-api.jar app.jar ENTRYPOINT [java,-Xms512m,-Xmx512m,-Djava.security.egdfile:/dev/./urandom,-jar,/app.jar]这里有几个细节基础镜像必须和编译 JDK 大版本一致JDK 8 项目就选openjdk:8-*不要选 17设置TZAsia/Shanghai否则容器内时间差 8 小时订单创建时间会集体漂移磁盘上的上传文件目录要挂载 Volume否则容器重建后用户图片全丢Docker Desktop 本地调试时如果项目是从 Windows 共享目录里挂载出来的要注意文件监听性能问题开发阶段直接用spring-boot:run更省事。4.4 商城环境的运维细节多环境配置、日志与备份上线前我会把 SpringBoot 的配置拆成多环境文件application.yml application-dev.yml application-prod.ymlapplication.yml里只放公共配置环境相关的数据库、Redis、对象存储地址放到对应 profile 文件里启动时通过SPRING_PROFILES_ACTIVE指定。这样能避免开发连生产库这种事故。另一个容易被忽略的是日志商城项目至少要把登录日志、订单操作日志单独记录出了问题能够追踪是谁在什么时间改了什么订单状态。数据库备份用任务计划或者平台自带的自动备份功能每天一次全量每次大版本发布前再做一次手动备份。5. 这套项目里的真实踩坑清单与顺序优化建议5.1 联调阶段最常见的三个问题第一个是跨域。管理后台开发时直接访问 C 端接口浏览器跨域报错配置好 CORS 后还要注意allowCredentials和allowedOrigin不能同时使用通配符。第二个是参数命名风格不统一后端习惯orderId前端写成了order_id被这种问题浪费的时间比技术难点多得多。从项目第一天起就约定接口文档规范查询参数用驼峰还是下划线一旦定了就不要改。第三个是日期时区问题后端new Date()存储的是 UTC前端拿到后如果不指定时区显示出来的时间就差了 8 小时。前后端约定统一用时间戳或者带时区的 ISO 字符串能省掉大量扯皮。5.2 如果重来一次我会把这些事前置做成这个项目后我复盘过有几个环节如果能前置整体周期至少缩短两成。接口文档先行。我们的问题是边开发边补接口文档前端等后端接口时经常靠猜。如果一开始用 Apifox 或 YApi 把接口定义好前后端并行开发根本没有阻碍。订单状态机先设计。商城项目的核心不是增删改查而是订单流程、库存扣减、支付回调这三件事。这三件事不提前讨论清楚后期每个接口都在为状态错乱打补丁。测试用例先写。不是说 TDD 那种严格流程而是核心订单流程的单元测试要在开发的同时就写。这个项目最贵的 bug 出现在支付回调重复执行上如果第一次写支付逻辑时就配上幂等校验和测试后面根本不用改。5.3 为什么这类项目会频繁出现在面试题里打开招聘网站搜SpringBoot和uniapp相关的面试题商城项目是出现频率极高的案例。原因很简单商城项目覆盖的知识面足够宽。SpringBoot 考自动装配原理、循环依赖、单元测试、集成 Redisuniapp 考多端差异、打包配置、生命周期、自定义组件后端考订单状态机、库存扣减、幂等性、定时任务。这些点恰好对应了中小团队实际开发中最高频的技术场景能完整讲清楚一个商城项目的人通常意味着他真实经历过联调、上架、排查线上问题的全过程。这也是我坚持用 SpringBoot uniapp 做商城项目的原因。它不炫技但每一个模块都是将来开发其他业务系统能复用的底子。真正做完一遍你的收获会比看十篇教程都大。最后分享一个我个人的小习惯在 uniapp 项目里尽量用条件编译处理端差异比如#ifdef MP-WEIXIN、#ifdef APP-PLUS把不同端的特殊逻辑隔离在小代码块里公共代码保持干净。这个习惯在后端多环境配置里也一样适用。商城项目表面上是技术问题本质上是对业务边界的理解和执行顺序的把握把这层想清楚你的代码会少很多补丁。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →