
后台经常有人问我有没有一套既能拿来练手、又能真正常跑的商城源码最好技术栈主流一点别上来就是老掉牙的JSP那套。我最近整理了一套基于SpringBootVue3的多端商城系统PC端和移动端两个前端版本共用一套后端API源码结构比较完整商品、购物车、订单、支付回调、后台管理这些模块都有。这篇文章就围绕这套系统把技术选型、核心模块设计、双端实现方式、本地跑通步骤以及部署上线时的常见坑按照我实际维护这类项目的经验一次性讲透。适合正在学全栈的人、准备做毕业设计的学生以及想快速搭建一套商城原型给客户演示的开发者。我不会去贴那种零注释的完整项目代码因为几百个文件贴出来反而没用。我更想把代码里的设计逻辑、模块关系、联调关键点讲清楚——比如SKU到底怎么设计、下单时库存是怎么扣的、PC端和移动端为什么不做成一套响应式、Token过期以后前端要怎么静默刷新。这些才是你拿到源码之后真正需要看懂的地方。1. 项目定位与核心技术选型1.1 为什么后端是SpringBoot前端是Vue3这套商城系统的后端选SpringBoot基本是当下Java后端项目的主流答案。SpringBoot最大的价值在于约定优于配置内嵌容器、自动装配、起步依赖这些特性让项目从零搭建到跑起来的时间被压缩得非常短。商城这类业务涉及商品、库存、订单、支付、用户等多个领域用SpringBoot做分层开发Controller、Service、Mapper各司其职后期加需求、改逻辑都会清晰很多。另外Spring生态里现成的方案多Spring Security做登录鉴权、Redis做缓存和分布式会话、MyBatis-Plus操作数据库这些都能在商城系统里直接落位。前端选Vue3也是经过考量的。Vue3的组合式API解决了过去Options API在复杂业务中逻辑分散的问题比如购物车列表和结算操作关联密切用setup函数可以把相关的响应式变量和函数收拢在一起代码看起来很直观。加上Vue3对TypeScript的支持天然友好商城这种多模块项目字段类型多使用TS约束接口返回的数据类型联调时的沟通成本会大幅下降。Vite的开发服务器启动速度也比Webpack那一代明显更快改完代码热更新基本是秒级反映这对调试前端体验很重要。选型时有一个容易被忽略的点是生态和招聘的通用性。无论你是自己做项目积累经验还是团队进入新成员SpringBoot加Vue3这套组合的参考资料最多遇到问题可以快速找到同类场景的解决方案。我见过不少项目选型时追求冷门框架结果遇到一个奇怪Bug就卡住好几天社区资料少、插件不完整纯粹是给自己挖坑。商城系统的重点应该是业务逻辑和工程落地而不是在技术选型上标新立异。1.2 双版本思路两套前端还是响应式一套标题里说的PC端加移动端双版本很多人第一反应是做一套响应式页面宽度自适应解决。实际上对于商城这种交互复杂的系统PC端和移动端的用户习惯差异很大PC端屏幕大适合展示复杂的商品列表、后台管理的数据表格移动端屏幕小操作以触控为主更需要简化入口、大按钮、底部导航。硬塞进一套响应式页面结果往往是两端都用着别扭。这套系统采用两套前端工程的方式。PC端包含商城主站和管理后台使用Vue3加Element Plus这类面向桌面的组件库布局上走传统的前台加后台管理模式移动端单独建一个H5工程使用Vant这类移动端组件库按照移动端规范设计底部Tab栏、列表页和详情页。两个前端工程通过同一套后端API获取数据后端不需要关心请求来自哪个端只需要在需要时通过请求头中的客户端类型字段做轻微差异化处理。两套前端带来的维护成本增加是真实的但换来的用户体验提升也是实在的。如果你做的是展示型官网用响应式一套完全合理但商城这种涉及购物车、结算、个人中心多个流程的应用我更推荐双工程方案。这个项目里两个前端共用同一套后端接口API是按业务领域而不是按端来划分的比如商品列表接口同时服务PC端和移动端两端只是页面渲染不一样数据模型完全一致。实际运营中如果要改商品展示规则后端改一次两端同时生效并没有想象中那么麻烦。2. 后端核心模块与业务链路拆解2.1 商品体系从SPU到SKU的设计商城的商品模块是整条业务链的核心这里的设计几乎决定了后续购物车、订单、库存所有环节的复杂度。项目里采用SPU和SKU的标准电商模型。SPU是商品抽象概念比如一部手机就是一个SPU表示“有什么商品”SKU则是具体可购买的规格项比如“黑色、256GB”版本对应一个SKU。用户在商品详情页选择颜色、内存时实际上是在SPU下面选择具体SKU。数据库层面商品信息被拆成几张表商品主表存SPU的基础信息如标题、主图、分类、状态SKU表存具体规格对应的价格、库存、编码商品属性表存动态规格名和规格值例如颜色、内存这类不固定的属性。为什么不能把规格直接写成字段放在商品表里因为不同商品的属性维度完全不同手机有内存颜色衣服有尺码统一做成固定字段会导致表结构僵化扩展性极差。用属性表存储后端服务只需要按照SPU ID查询属性列表就能动态渲染任意品类的规格选择界面。库存处理是这个模块最敏感的部分。SKU表的库存字段是数据库中的硬性数据每次下单扣减都需要保证原子性避免超卖事故。项目里常见的做法是使用UPDATE语句进行库存扣减SQL中带条件判断当前库存大于等于购买数量。这样既干净地完成了扣减又防止了并发下超卖。如果直接在Java代码里先查询库存再判断是否充足并更新在高并发下会有大量请求同时读到库存8然后同时执行减库存操作最终导致实际售出超出库存这是典型的并发踩踏问题。2.2 购物车、下单与订单状态流转购物车这层设计看似简单但存数据库还是存Redis需要权衡。这套系统选择把购物车数据存入MySQL表结构为用户ID加SKU ID加数量加勾选状态。原因在于商城用户购物车条目不多数据库完全够用同时购物车需要跨设备保持一致如果存浏览器本地换一台设备购物车就不见了体验很差。Redis适合存高频访问的临时数据购物车既需要持久化又需要偶尔查询MySQL反而更踏实。购物车接口设计成树状返回外层是商品条目列表内层携带当前价格、小计、库存状态前端渲染时直接展示即可。下单流程则是一串事务性很强的操作。用户提交订单后后端需要做几件事校验购物车数据、检查库存、锁定库存并生成订单记录、同时生成订单明细。这个流程必须在一个事务中完成任意一步失败都要回滚保证库存和订单数据的强一致。订单生成后状态置为待支付同时创建支付单。用户可能中途放弃支付因此项目里用定时任务扫描超时订单超过比如30分钟仍未支付就自动取消并回补库存避免无效订单长期占用库存。订单状态在代码里通常用一个状态字段加枚举类来维护。枚举中定义待支付、已支付、已发货、已完成、已取消等状态。为什么不用简单的int字段加注释因为订单状态的流转有明确的方向限制比如已支付订单不能跳回待支付已取消订单不能再支付。把状态定义在枚举里每段判断逻辑就是可读的语义化代码而不是到处散落魔法数字。支付回调处理时也要做幂等校验同一笔订单回调可能因为网络重试到达多次后端必须判断当前状态再决定是否更新否则一个订单会被重复标记为已支付。2.3 用户体系与JWT登录鉴权用户模块在商城项目里的职责不只是注册登录还包括地址管理、浏览历史、会员等级等延伸业务。后端用户表主要存储账号、密码密文、昵称、头像、手机号等基础信息。密码存储我一般用加盐哈希而不是可逆加密注册时生成随机盐值拼入密码后计算哈希值数据库泄露时攻击者也无法反推出明文密码。项目里没有自研复杂的权限框架管理后台和移动端用户通过角色字段区分操作管理接口时后端判断是否具备管理员角色。登录鉴权采用JWT方案这也是当前前后端分离项目的常见选择。用户登录成功后后端生成一个包含用户ID、角色、过期时间的令牌返回给前端前端存储在本地并在每次请求的Authorization头中带回后端通过拦截器统一解析和校验。相比Session方案JWT不需要在服务端保存会话状态天然适合分布式部署但它的缺点是令牌签发后不能在服务端主动失效所以项目里设置了合理的过期时间并在前端统一处理Token过期后的重新登录跳转。管理后台的权限控制比用户端复杂一些需要同时拦截未登录请求和越权操作。我在项目中习惯用一个注解加拦截器的方式来实现简单的权限控制例如在管理端接口上标注需要管理员角色拦截器解析出当前用户的角色后做匹配校验不满足直接返回无权限提示。这种轻量方案避免引入重型权限框架适合商城业务的所有权限场景。有更复杂的多角色权限需求时再考虑引入专门的权限框架来维护角色和菜单之间的关系目前这套结构已经足够。3. 前端双版本PC端与移动端的实现分工3.1 PC端商城与管理后台放在一个工程里PC端工程不仅面向普通用户浏览购买还承载了管理员的运营后台这是商城主站的一个常见组合。为什么把前台和后台放在同一个前端工程里因为两者共享大量基础设施例如请求封装、登录态管理、组件封装。项目里通过路由配置区分两块区域普通用户访问首页、商品列表、购物车、订单等用户端页面管理后台页面全部挂在独立路由前缀下由菜单和路由守卫控制访问没有管理员身份的登录用户跳转后台时会被拦截并提示。管理后台的核心页面在源码中主要集中在商品管理、订单管理、用户管理、分类管理这几块。商品管理页面要能查看SPU列表、编辑SKU价格和库存、上下架商品表格组件配合弹窗表单实现这些交互很合适订单管理页面则要按订单状态筛选支持发货操作和查看订单详情后端为每个列表接口都做了状态条件筛选参数前端切换Tab时只需重新请求对应状态的数据。这种前后台混合在一个工程的做法对中小型商城来说维护效率比拆成两个独立工程更高。PC端的页面布局采用经典的用户浏览模式顶部导航栏包含Logo、搜索框、购物车入口和用户信息中间是内容区域底部是辅助链接。商品列表页使用栅格布局每个商品卡片展示图片、标题、价格、销量点击进入详情页。这个布局实现起来不复杂但是性能细节要留意商品图片数量多必须使用懒加载方案否则首页首屏的图片请求量会拖慢加载速度。项目里图片懒加载通过自定义指令实现在图片即将进入视口时才去加载资源实测下来首屏白屏时间明显减少。3.2 移动端H5商城适配与交互细节移动端H5商城是独立工程组件库全部使用轻量级的移动端组件设计语言更贴近App风格。页面结构采用典型的底部Tab栏布局一级入口是首页、分类、购物车、我的。为什么移动端必须做底部Tab栏因为拇指操作的范围集中在屏幕下半部顶部入口在单手操作时很难触达尤其是手机尺寸越来越大之后这种差异很明显。底部Tab栏固定定位页面切换时只刷新内容区整个交互体验接近原生App。样式适配这块项目里采用基于视口单位的适配方案。设计稿按750px宽度出图开发时把尺寸换算成vw单位或者通过PostCSS插件自动转换不同屏幕宽度下页面元素等比缩放。字体方面要注意正文内容没有使用固定px值而是使用相对单位避免大屏手机上文字过大导致布局错乱。移动端还有一个常见问题是点击延迟项目里通过设置合适的触摸事件和meta视口配置来规避保证用户点击商品卡片时能快速进入详情页。移动端的购物车和结算流程在交互上与PC端有差异。购物车页面要支持左右滑动删除、数量加减、勾选结算商品组件库的滑动单元格可以直接满足这些交互结算页则省去了PC端那种冗长的表格展示改用分区块方式展示收货地址、商品清单、金额汇总底部固定一个提交订单按钮。提交订单前前端要先把用户选择的收货地址ID、商品SKU列表和数量整理成后端要求的请求结构并且做好前端必填校验能拦截的错误尽量在提交前就提示减少无效请求打到后端。3.3 API封装与全局状态管理两个前端工程都采用了Axios作为HTTP请求库但在封装细节上有不少共同经验。项目里统一把API请求收敛到独立的api目录下而不是在业务组件里直接调用axios这样后端接口路径一旦调整只需修改一处。请求拦截器负责注入Token响应拦截器负责统一处理错误码例如后端返回401时清除本地登录数据并跳转登录页返回业务错误码时弹出对应提示。这套机制保证了业务代码里不会到处是重复的错误处理判断代码。全局状态管理用的是Pinia。在商城前台购物车数量、用户信息、Token状态都需要在多个页面共享直接用组件props传递会非常麻烦。Pinia中定义用户模块和购物车模块分别管理登录状态和购物车条目任意组件都能通过Store读取和修改状态。例如顶部导航购物车角标数量用户加入购物车后更新Store中的数量所有引用这个状态的组件自动同步刷新不需要手动触发事件通知这是状态管理最实用价值的体现。环境配置上两个前端工程都预留了开发、测试、生产环境变量文件。开发环境代理后端接口地址例如把本地API请求转发到本地后端的8080端口从而解决跨域问题生产环境则改为同域访问或者请求真实域名后缀的API。使用环境变量而不是硬编码地址好处是打包时按环境自动注入对应地址代码提交到仓库里也不会暴露真实服务器接口地址。配置文件里区分VITE_API_BASE_URL这类统一前缀后期切换到不同环境只需修改一个变量。4. 源码实操本地运行、接口联调与上线部署4.1 后端代码结构与接口规范一套商城源码拿到手第一步别急着运行先把目录结构读懂。后端代码按照经典分层组织controller包接收前端请求并做参数绑定service包承载业务逻辑事务边界一般都标注在service层的方法上mapper层使用MyBatis-Plus单表操作直接继承BaseMapper多表联查时写XML或注解SQL。实体类放在entity包视图对象放在vo包DTO和VO的区别在实际项目里很容易被新手忽略DTO用于接收前端传入的数据VO用于封装返回给前端的数据结构分层清晰后前后端数据结构不容易乱。接口规范在源码里执行得比较一致。所有接口以/api前缀开头用户端和管理端的接口通过路径区分未登录接口和需登录接口也通过路径或者注解区分。数据返回统一封装为code加message加data的结构前端响应拦截器先判断code再决定是提取数据还是抛出错误信息。分页接口统一接收当前页和页大小参数返回分页对象前端表格组件只需要把响应数据映射到绑定的数据源即可。这种规范一旦成立后续新增接口的成本会很低新来的开发者只看几个现有接口就能模仿着写。4.2 本地环境搭建五步走本地运行这套系统依赖几个基础环境JDK、Maven、Node、MySQL、Redis。要是你是第一次跑这种全栈项目建议按顺序操作。第一步创建数据库并导入项目目录中提供的初始化SQL脚本脚本包含了所有业务表结构以及测试数据省去手写建表语句的麻烦。第二步修改后端的配置文件重点确认数据库连接地址、用户名密码以及Redis连接信息本地单机环境下密码默认留空或者简单的测试密码都可以。启动顺序上我一般建议先启动Redis再启动后端服务。后端使用SpringBoot的启动类直接运行如果控制台没有报错并且日志里出现启动完成时间就说明后端已经就绪。此时可以在浏览器访问后端接口地址配合可视化的接口调试工具逐一测试登录、商品列表、添加购物车这几个基本接口确认链路正常。第三步是前端工程安装依赖进入PC端或移动端目录运行包管理工具安装命令之后启动Vite开发服务器控制台会输出本地访问地址。最后一步是配置前端环境变量中的API地址开发环境指向本地后端地址。这里有一个需要特别注意的点Vite的默认开发服务器地址是localhost如果你用手机在同一局域网测试移动端页面必须启动时指定监听地址为局域网IP否则手机无法访问页面。测试时手机访问的API地址也需要指向电脑的局域网IP而不是localhost这个问题我见过太多人在联调移动端时被卡住。4.3 服务器部署与反向代理本地跑通之后部署到服务器是另一个常见的需求场景。后端部署较为直接项目打包后生成可执行JAR文件服务器上需要准备JDK运行环境使用启动命令运行即可。为了不阻塞命令行会话我习惯使用后台运行方式启动并在命令中指定配置文件例如区分生产环境的数据库地址和Redis地址。如果服务器内存紧张可以给JVM设置初始内存和最大内存参数商城类的典型服务设置为512MB以内也可以正常运行。前端部署时构建产物是一堆静态文件打包后生成dist目录把文件放到Web服务器指定的站点目录即可。我常用Nginx承载静态资源并反向代理API请求。配置时给前端路由启用history模式要添加try_files回退规则否则刷新页面会报404。另外一个非常重要的配置是上传文件大小限制商城系统必然涉及图片上传功能Nginx默认请求体大小限制只有1MB上传较大的图片会直接报413错误在nginx配置中修改client_max_body_size可以解决这个问题。生产环境还有几个保障措施值得留意。HTTPS证书是必须考虑的事这个要看实际部署条件有条件的情况下列表页、详情页、结算页这些涉及隐私数据的页面走HTTPS是基本要求。部署完成后要顺手验证几个关键链路能否正常注册登录、商品图片能否正常显示、下单后订单状态是否能推送到待支付。我见过部署完才发现静态资源路径不正确整个页面没有图片的情况这类问题根源通常在于前端打包时资源引用路径用了绝对路径而Nginx站点目录结构没有与之对应调整。5. 常见问题与实战避坑指南5.1 跨域、上传、Token联调阶段三大高频问题前后端分离项目联调阶段最常遇到的拦路虎就是跨域。本地开发时最省事的解法是走前端代理Vite开发服务器把API路径代理到后端地址浏览器里看不出跨域生产环境则由Nginx反向代理统一转发也不会触发跨域。如果你习惯在联调阶段直接让前端访问后端地址就需要后端正向处理跨域请求配置允许的跨域来源和请求头。需要强调的一点是跨域配置别图省事直接用星号生产环境应该限制为真实的域名来源。文件上传失败是第二类高频问题。这背后可能有三种原因一是Nginx请求体大小限制导致413错误解决办法已经说过二是后端允许上传的文件大小配小了在配置文件中可以调大三是上传目录没有写入权限后端起服务时使用的是系统用户上传目录如果是root所有且权限不足就会报IO异常。排查时先看后端日志再逐步检查Nginx配置和后端配置这类问题解决起来其实很快。Token相关的坑主要集中在过期处理和并发请求上。后端返回401时前端弹窗提示并跳转登录页是最基本的处理但如果当前页面同时在等待多个请求多个请求都拿到401会触发多次跳转和重复弹窗体验很差。项目里封装请求时会对401做去重处理导出一个可重入的跳转函数或者拦截后只让第一个请求负责跳转其余请求直接拒绝。Token即将过期时静默刷新的方案也可以做但会引入刷新接口和并发锁的复杂度基础的商城项目先用短过期加跳转登录的方案已经够用。5.2 多端适配中容易忽略的细节PC端和移动端共用后端API时字段的消费方式不同会造成一些尴尬情况。例如商品详情的富文本内容PC端用大图展示没问题移动端需要控制图片宽度否则大图会把页面撑破。项目里商品图片保存在富文本中是完整路径移动端通过CSS样式对详情区域内的图片设置最大宽度解决。另一个例子是列表接口一次返回的数据量移动端网络环境通常比PC端弱如果直接复用同一接口的默认返回条数加载时间会偏长这时需要在移动端请求时传更小的分页参数。时间格式化也是两端容易表现不一致的地方。后端返回时间戳或标准时间字符串PC端表格中直接显示完整日期时间可以接受移动端则更需要相对时间比如几分钟前、昨天这种描述方式。项目里没有让后端针对不同端返回不同格式而是前端各自处理格式化移动端的组件库提供了现成的时间格式化能力按需在当前端做转换即可。适配方面还有一个容错处理同一接口在不同尺寸屏幕上展示可能出现数据渲染溢出或元素错位。PC端的表格直接平铺展示是合理的移动端同一数据就要改用卡片列表PC端的按钮可以带图标和长文案移动端按钮则尽量精简为小图标加短文案。这些细节在开发时多花一点时间最终呈现的效果差距非常大。5.3 安全性处理数据校验与统一异常商城系统的接口天然暴露在外网安全性这根弦不能松。数据校验方面项目里对待支付订单金额、购买数量、收货地址ID等关键参数做了后端校验而不只是依赖前端界面拦截。道理很简单前端限制只是用户体验优化攻击者完全可以绕过页面直接构造请求。后端收到参数后先进行格式和范围校验字符串长度、数值上下限、枚举值白名单都逐项检查校验不通过直接返回参数错误避免脏数据进入业务逻辑。SQL注入防护在项目中使用预编译语句MyBatis-Plus的Wrapper查询和XML中预编译的参数占位符都能有效防止注入攻击。还有一个容易被忽视的是批量插入和模糊查询场景代码拼接SQL字符串时必须提醒自己改成参数化操作。虽然框架自身有一定防御能力但日志打印和排查问题时看到的SQL都应该是预编译后的安全语句而不是拼接过用户输入的原始内容。统一异常处理在项目里是全局生效的。定义全局异常处理器拦截业务异常、参数校验异常、未知异常三类情况分别返回对应的提示信息和状态码。这样做的好处有两个一是前端只需要按照约定好的错误结构解析即可不用面对各种格式的异常堆栈二是后端日志可以完整记录异常上下文但在返回给前端的消息中不会泄露堆栈等内部信息同时避免异常细节暴露给用户。5.4 常见问题速查表下面这份表格整理了我在运行和部署这套商城源码过程中最高频遇到的问题以及对应的解决方案。问题现象可能原因解决方案前端请求接口报跨域错误开发环境未走代理后端未配置允许来源优先走前端代理后端按需配置CORS并限定域名页面刷新404前端启用history路由Web服务器未回退在Nginx配置中添加try_files规则上传图片报413Nginx请求体大小限制调整client_max_body_size配置点击登录无反应后端服务未启动或API地址配置有误检查后端日志确认前端环境变量指向正确地址下单之后库存没扣事务边界没包住库存扣减和订单生成确保扣库存和生成订单在同一事务方法中管理后台提示无权限当前账号角色不是管理员检查用户表角色字段管理员账号使用对应角色移动端访问后端失败手机和电脑不在同一局域网或API地址为localhost改为局域网IP并确保防火墙放行端口支付回调重复处理支付渠道重试导致同一回调多次送达校验订单状态做幂等判断已支付订单直接返回成功这个速查表不可能覆盖所有问题但覆盖了从开发到上线的绝大多数高频场景。你实际遇到问题时优先看后端日志日志是定位故障最直接的入口。其次是看浏览器控制台的请求响应状态码和响应内容基本能判断是后端逻辑问题、网络问题还是前端代码问题。我个人的体会有两点。第一这类前后端分离的商城项目真正的关键点并不在于某个页面的花样而在于数据流是否顺畅从商品浏览到下单到支付每一步的数据传递都不能断所以调试时建议一家一家的链路慢慢过先保证主干链路通了再完善细节。第二源码拿到手不要只满足于跑起来一定要去看订单状态是怎么流转的、库存是怎么防超卖的把这两个核心机制理解了商城项目的核心逻辑你就真正吃透了后续在此基础上加优惠券、秒杀、分销这类扩展功能时也能得心应手。实际运行一段时间把日志和监控数据收一收会有很多意想不到的收获。