news 2026/10/13 7:15:26

SpringBoot+Vue+MyBatis+MySQL的家电销售展示平台实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot+Vue+MyBatis+MySQL的家电销售展示平台实战

做了这么多年管理系统开发,SpringBoot+Vue的组合见得太多,但专门面向“家电销售展示”场景的完整项目源码反而没那么普及。前阵子刚把一个基于SpringBoot+Vue+MyBatis+MySQL的家电销售展示平台管理系统从零搭完,从需求梳理、数据库建模到前端页面、后端接口,再到部署上线,整套流程走下来有不少值得记录的东西。这篇博文就把这个项目完整拆开讲,适合正在做毕业设计、想转电商类管理系统的初学者,也适合准备接家电行业信息化订单的开发者参考。

先说清楚这个项目解决的是什么问题。家电行业的销售展示和普通电商不太一样,商品参数多、规格复杂,既有空调冰箱这种大件,也有电饭煲吸尘器这种小家电,而且线下门店和线上展示往往需要共用一套库存。单纯的商城或单纯的进销存都搞不定,必须有一个同时兼顾“前台展示”和“后台管理”的平台。这个系统就是这样设计的:前台给客户浏览家电、查看详情、下单,后台给管理员维护商品、处理订单、管理分类和库存,前后端分离,接口驱动,业务链路完整。

1. 这类系统为什么需要“展示+管理”双端设计

1.1 家电销售业务的真实痛点

我接到这个项目需求时,对方第一句话就是“我们要一个官网一样的展示页面,还得能管商品和订单”。这是很典型的口径,翻译成技术语言就是:前台页面要能展示商品、品牌、分类,最好有搜索和筛选;后台要能对商品做增删改查,能管理订单状态,能看库存。如果只做展示页,数据维护就得靠开发改数据库,运营根本玩不转;如果只做管理系统,客户又没法自助查看商品,销售效率太低。所以展示端和管理端缺一不可。

另外一个痛点是家电商品的属性特别杂。一台冰箱有容量、能效等级、制冷方式、面板材质,一台电视有屏幕尺寸、分辨率、刷新率、HDMI接口数量。如果按照传统电商那种统一的“规格名-规格值”表去设计,查询起来会非常绕。这个项目里我采用了“公共字段+扩展参数JSON”的方式,既保证了列表页需要的关键字段可以直接检索,又照顾了不同品类家电之间的差异性。

1.2 展示端与管理端的边界划分

这个系统的边界划分原则很简单:展示端只做读操作,管理端负责写操作。前台所有请求都走查询接口,下单也只在用户确认购买时才会触发写库;后台则承担了商品管理、分类管理、订单处理、轮播图配置、用户管理这些重活。

从工程结构上看,一个SpringBoot服务同时提供两套接口路径,/api/portal/**是展示端接口,/api/admin/**是管理端接口。这样做的好处是前后端分离后,同一个后端可以服务多个前端项目,将来如果要做小程序端或移动端H5,直接复用普通用户接口就行。而且权限拦截也方便,管理员接口统一加鉴权,普通接口只做基础校验。

1.3 适合谁学习和复用

如果你是刚学完SpringBoot和Vue、想找一个能写进简历的实战项目,这套系统非常合适。它的技术栈主流、业务完整、代码量适中,不像那种纯CRUD的demo,也不像大型电商平台那样复杂到看不完。如果你是要接家电行业的信息化项目,这套源码里关于商品参数、订单状态流转、库存扣减的这些设计可以直接抄作业,很多小家电企业的管理需求并不会比这个更复杂。

2. 技术栈选型:SpringBoot+Vue+MyBatis+MySQL的适配逻辑

2.1 后端为什么选SpringBoot

SpringBoot在Java生态里已经是事实标准,尤其是这类管理系统的首选。它把Spring繁琐的XML配置全部干掉,用自动配置和starter机制把依赖管理变得极其简单。这个项目里用到的核心依赖就是Web、MyBatis、MySQL驱动、Lombok,再加一个JWT相关的工具包,加起来没几行配置就能跑起来。

选择SpringBoot还有一个特别现实的理由:招人容易、资料多。不管是后续团队接手还是自己做毕业设计找参考资料,社区里踩坑记录都非常充足。遇到奇奇怪怪的问题,搜一下基本都能找到答案。这是技术选型里很容易被忽视但实际很重要的因素。

2.2 前端为什么选Vue

Vue在前端框架里的优势是上手曲线平缓、模板语法直观。这个项目要做的不只是几个静态页面,还有商品列表、筛选条件、购物车、后台管理表格这些高频交互组件,用原生JS写会非常痛苦,用Vue配合Element UI可以极大提升开发效率。Vue的双向绑定在做筛选条件时尤其方便,选品牌、选价格区间、选能效等级,数据层直接维护一个queryParams对象,页面自动响应更新。

这里要说明一下,我用的Vue版本是Vue 2 + Element UI,而非Vue 3 + Element Plus。选Vue 2的原因很朴素,这个系统要求兼容性稳定、组件生态成熟,Element UI的文档和示例最多,开发时遇到问题好解决。如果是从零开始的新项目,上Vue 3 + Vite + Element Plus也完全可以,只是组件API有些差异,项目里就不混着写了。基于Vue的组件化机制,同一套后端接口可以无缝对接两种前端框架。

2.3 持久层为什么选MyBatis

MyBatis和JPA(Spring Data JPA)之间我最终选了MyBatis,原因是家电商品的查询条件太灵活了。商品名模糊查询、品牌筛选、价格区间、分类筛选、能效等级,这些条件用户想怎么组合就怎么组合,MyBatis的动态SQL对这种多条件组合查询的支持非常自然,<where>加<if>标签就能搞定,SQL的可控性强,性能调优也方便。

MySQL作为数据库没有悬念,项目体量在中小型应用范围内,MySQL足够稳定,部署成本低,运维资料丰富。家电展示平台往往是读多写少,MySQL默认的B+树索引结构对这种场景的支撑非常到位,配合Redis做缓存是后续优化方向,但前期单库单表完全够用。

表2-1列出这套选型和常见替代方案的对比,方便你判断自己的场景是否适合:

对比项本方案替代方案选择理由
后端框架SpringBootSSM手写整合SpringBoot自动配置省去大量XML,开发效率高
前端框架Vue 2 + Element UIReact / Vue 3Vue模板语法简单,Element UI组件齐全
持久层MyBatisMyBatis-Plus / JPA动态SQL灵活,SQL可调优;MyBatis-Plus可后续引入
数据库MySQLPostgreSQL部署简单,生态成熟,学习成本低
鉴权方式JWT无状态Session + Redis前后端分离下JWT更好扩展,支持多端登录

2.4 项目整体结构一览

后端是标准的Maven多模块?不,这里我用的是单模块,因为项目规模还没到需要拆分的程度。包结构按职责划分,很直观:controller、service、mapper、entity、dto、vo、config、common、utils。前端的src下按视图划分:views/portal放展示端页面,views/admin放管理端页面,components放公共组件,router做路由配置,api封装axios请求。

我把这种结构叫做“按端分层,按业务聚合”。后端按技术层分包,前端按业务端分包,各管各的,互不干扰。前后端通过JSON格式进行数据交换,接口文档直接用Swagger生成,联调的时候非常省心。

3. 数据库设计:家电商品特殊属性怎么建模

3.1 核心表结构总览

这个系统一共设计了8张核心表:用户表、分类表、品牌表、商品表、商品参数表、订单表、订单项表、轮播图表。外加一个轮播图配置表虽然简单,但实际业务里轮播图经常要改,做成表比写死在配置里科学得多。

商品表的设计是这个项目的重头戏。我把商品通用信息(名称、主图、价格、库存、销量、上架状态、创建时间)放到商品表,把特殊属性(能效等级、制冷方式、屏幕尺寸这些)放到商品参数表,用商品ID关联。这样列表页查商品时不需要解析JSON,详情页读取参数时也只需要一次关联查询,兼顾了性能和灵活性。

3.2 分类与品牌的设计细节

分类设计成两级:一级分类(大家电、厨卫电器、生活小电)和二级分类(空调、冰箱、洗衣机、电视、电饭煲、破壁机等)。我用了很简单的parent_id自关联方式,没有引入嵌套集模型,因为这个业务场景下分类层级浅、数量少,自关联完全够用,查询也直观。

品牌表单独建是为了前台筛选方便。家电领域品牌认知度很高,用户会在列表页直接按品牌过滤,也有按品牌查看全部在售商品的需求。商品表存brand_id外键,品牌表有品牌名称和Logo图字段,前台品牌筛选就是一次关联查询的事。

3.3 商品参数表的JSON vs 关联表

这里我犯过选择困难。格力空调参数方案有两种:一种是商品参数表每行存一个param_name和param_value,完全范式化,将来想按参数搜索也行;另一种是直接在商品表加一个params字段存JSON。最终我选了折中方案——参数表存JSON,但不是一整个大JSON,而是拆成basic_params、spec_params这种按组划分的JSON字段。

这么做的原因是家电详情的参数展示是“分组式”的,基本参数一组、规格参数一组、能效信息一组,按组存JSON在读取时天然就能按组取出,无需前端再做结构转换。如果将来要做参数级筛选,可以再抽一张参数映射表,属于演进方向,不影响当前设计。

商品表的关键字段如表3-1所示:

字段名类型说明
idbigint主键自增
category_idbigint二级分类ID
brand_idbigint品牌ID
namevarchar(200)商品名称
subtitlevarchar(500)卖点副标题,列表页展示
main_imagevarchar(500)主图URL
pricedecimal(10,2)售价
stockint库存数量
salesint销量
statustinyint1上架 0下架
is_hottinyint首页推荐标记

3.4 MyBatis映射文件里的两个典型场景

第一个是商品列表的多条件查询,这是MyBatis动态SQL最精华的场景。<where>标签自动处理AND前缀,每个<if>判断用户是否传入了对应条件,组合起来就形成了“品牌+分类+价格区间+关键字”的联合筛选。

<select id="selectPageList" resultType="com.demo.entity.Product"> SELECT p.*, b.name AS brand_name, c.name AS category_name FROM product p LEFT JOIN brand b ON p.brand_id = b.id LEFT JOIN category c ON p.category_id = c.id <where> <if test="keyword != null and keyword != ''"> AND p.name LIKE CONCAT('%', #{keyword}, '%') </if> <if test="brandId != null"> AND p.brand_id = #{brandId} </if> <if test="categoryId != null"> AND p.category_id = #{categoryId} </if> <if test="minPrice != null"> AND p.price &gt;= #{minPrice} </if> <if test="maxPrice != null"> AND p.price &lt;= #{maxPrice} </if> <if test="status != null"> AND p.status = #{status} </if> </where> ORDER BY p.id DESC </select>

第二个是批量更新库存。订单支付成功后要扣减库存,如果逐条循环更新,在高并发场景下效率低且容易造成数据不一致。在MySQL的UPDATE语句中把库存值直接写成stock - #{quantity},配合WHERE stock >= #{quantity}加上库存充足的校验,这是最经典的防超卖写法之一。

<update id="decreaseStock"> UPDATE product SET stock = stock - #{quantity} WHERE id = #{productId} AND stock &gt;= #{quantity} </update>

4. 后端SpringBoot核心实现:从登录鉴权到订单流程

4.1 包结构设计与职责划分

后端代码的包结构直接决定了后续维护的幸福感。我的划分方式是比较标准的:entity是数据库实体类,dto是接收前端参数的传输对象,vo是返回给前端的视图对象,mapper是MyBatis接口,service是业务逻辑层,controller是接口入口层。

common包里放统一返回结果Result、统一异常处理GlobalExceptionHandler和业务异常类。这里的统一返回结果很重要,前后端分离后必须约定一个标准的数据包装格式,否则前端每个接口都要单独解析,非常容易出问题。我的格式很简单:{ code: 200, message: "success", data: {...} },前端axios拦截器统一判断Code后再决定是走成功逻辑还是弹错误提示。

4.2 登录鉴权:JWT方案的具体写法

这个系统分两种角色:普通用户和管理员。普通用户登录后可以在前台下单和管理个人资料,管理员登录后进入后台管理界面。我没有用Session方案,而是选择了JWT,因为前后端分离后JWT天然无状态,后端不需要维护Session存储,扩容也更方便。

登录接口的逻辑是:接收用户名密码,校验成功后生成一个Token,把用户ID和角色写进Token;前端拿到Token后存在LocalStorage里,每次请求在axios拦截器中加上Authorization: Bearer <token>头。后端用一个拦截器解析Token,解析失败直接返回401。

@Component public class JwtInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (!(handler instanceof HandlerMethod)) { return true; } String token = request.getHeader("Authorization"); if (token != null && token.startsWith("Bearer ")) { token = token.substring(7); try { Claims claims = JwtUtil.parseToken(token); request.setAttribute("userId", claims.get("userId")); request.setAttribute("role", claims.get("role")); return true; } catch (Exception e) { response.setStatus(401); return false; } } response.setStatus(401); return false; } }

这个拦截器需要注册到WebMvcConfigurer中,并且做好路径放行。放行规则是:/api/portal/**下的商品查询接口全部放行,用户登录接口放行;/api/admin/**必须校验Token且角色为管理员;/api/portal/order/**校验Token但不强制管理员角色。

4.3 商品展示接口的设计思路

前台商品接口的核心是GET /api/portal/product/page,参数是pageNum、pageSize、keyword、brandId、categoryId、minPrice、maxPrice、sort。排序参数我支持了三种:默认最新、销量倒序、价格升序/降序。这些排序在SQL层的实现方式不同,默认和销量排序可以直接查询,价格排序需要单独排序字段,但实现起来也都不复杂。

详情页接口是GET /api/portal/product/{id},返回的VO里包含了商品基本信息、参数JSON、品牌名、分类名。前端详情页要做的是把参数JSON按组渲染成表格,这个逻辑放在前端还是后端?我选择放后端,在VO里直接定义好basicParams和specParams两个JSON对象,前端只需要循环渲染,省去前端解析拼接的步骤。

首页还需要一个推荐商品接口,逻辑很简单:查is_hot=1且status=1的商品,按销量排序取前8个。这是典型的读多写少场景,后续可以加Redis缓存,不过现阶段直接查MySQL也毫无压力。

4.4 订单流程与事务处理

订单是这个系统业务流程最复杂的部分。用户从购物车提交订单,流程是:校验购物车商品和库存、计算总价、生成订单主表记录、生成订单项表记录、扣减库存、清空购物车。任何一步失败都应该回滚,所以这里必须加@Transactional。

@Transactional(rollbackFor = Exception.class) public Long createOrder(OrderCreateDTO dto) { // 1. 校验用户地址信息 // 2. 查询购物车明细,校验商品状态和库存 // 3. 计算总价 // 4. 保存订单主表,状态为待支付 // 5. 保存订单项列表 // 6. 批量扣减库存 // 7. 清空购物车 return order.getId(); }

这里要特别提醒,@Transactional只对RuntimeException和Error回滚,如果方法内有受检异常被捕获后不抛出,事务不会回滚。我写的rollbackFor = Exception.class就是把回滚范围扩大到所有异常,这是实际项目中更安全的写法。

订单状态我用整数表示:0待支付、1已支付/待发货、2已发货、3已确认收货、4已取消、5已退款。状态流转的规则是:待支付可以取消,支付后变为待发货,发货后用户可以确认收货,订单完成后可以申请售后(退款/退货)。每个状态变化都在订单日志表里记录一条,方便后期排查问题。

5. 前端Vue页面与交互逻辑拆解

5.1 前端整体路由规划

Vue Router的路由分了两块:/portal下的展示端页面和/admin下的管理端页面。展示端包括首页、商品列表页、商品详情页、购物车页、订单确认页、订单列表页、登录注册页;管理端包括商品管理、分类管理、品牌管理、订单管理、轮播图管理、数据统计概览。

管理端路由统一加一个meta: { requiresAuth: true, role: 'admin' },配合路由守卫做权限控制。前端守卫判断用户是不是管理员,不是就重定向到登录页。我不建议把安全完全交给前端,前端守卫只是用户体验层面的拦截,真正的权限校验必须以后端的JWT拦截器为准。

5.2 商品列表页的组件化设计

商品列表页是前端最复杂的页面。顶部是搜索栏,左侧是品牌筛选和分类筛选,主体是商品卡片网格,右下角是分页组件。我把“筛选条件栏”和“商品卡片”拆成了独立组件,列表页本身只负责维护筛选状态并刷新数据。

筛选栏组件使用了Vue的v-model语法,父组件传递queryParams对象,子组件修改后触发更新,父组件监听变化后重新调用接口。这个交互在Element UI里做起来很顺手,表单组件和按钮组件都是现成的。

商品卡片组件接收一个product对象,展示主图、名称、副标题、价格和销量,点击跳转到详情页。卡片组件看起来简单,但有细节值得注意:主图要用object-fit: cover固定宽高比,否则不同尺寸的图片会把页面撑乱;价格展示有两段,一个小的¥符号加一个大号数字,排版效果比直接拼字符串好看很多。

5.3 购物车与结算流程的前端实现

购物车我用的是后端存储方案,购物车记录存在数据库表中,用户登录后可以跨设备查看,而不是存在LocalStorage里。在前端层面,购物车页维护一个选中商品列表,用户勾选商品后点击结算,前端把选中项的ID列表传给订单确认页。

订单确认页是整个前端逻辑最密集的页面:展示商品清单、选择收货地址、计算总价、提交订单。这里有个典型问题:用户停留时间长了,商品价格可能已经变化,所以不能直接用前端缓存的价格来提交,而是要传商品ID+数量,由后端重新计算价格。前端只做展示,计算权必须在后端。

提交订单成功后跳转到一个模拟收银台页面,这里没有接第三方支付,而是提供一个“模拟支付”按钮,用户点击后前端调用订单支付接口,后端把订单状态从待支付改为待发货。如果要接入真实支付,只需把支付接口里的逻辑替换成调用微信支付或支付宝的统一下单API,其余订单流程完全不用改动。

5.4 管理后台表格与表单的实现细节

管理后台的本质就是一堆表格加表单,但写得好不好差别很大。商品管理页的表格我用了Element UI的el-table,列包括商品图片、名称、价格、库存、销量、上下架状态、操作按钮。操作按钮包括编辑、上下架切换、删除。删除用的是二次确认弹窗,防止误操作。

商品编辑表单是后台最复杂的表单。基本信息部分有商品名、副标题、分类、品牌、价格、库存、主图上传、参数配置。分类选择用的是级联选择器,因为只有两级,数据量很小,一次拉全量即可。图片上传走的是后端/api/admin/upload接口,后端把文件存到本地磁盘,同时返回访问URL。这里关于图片存储的坑,我放到下一节踩坑部分专门说。

6. 实际开发中踩过的坑:完整排查链路还原

6.1 MyBatis动态SQL条件全空导致的全表查询

开发中第一次遇到的问题是商品列表接口的SQL。原本想的是用户不传任何条件时应该返回空列表或默认显示部分商品,但实际运行时发现,当所有筛选条件都为空时,<where>标签里一个条件都没有,SQL就变成了SELECT * FROM product,把下架商品和全部历史数据都查出来了。

排查链路是这样的:前端列表页第一次加载时没有传任何条件,接口返回了所有商品,其中包含大量status=0的下架商品。我先检查前端的请求参数,发现queryParams对象里所有字段都是空字符串和null,然后检查后端的ProductQueryDTO,发现确实一个条件都没进<if>。问题定位到SQL本身——<where>里如果没有条件,MyBatis不会自动加WHERE但也不会帮你过滤状态。解决方案是在SQL里加一个强制条件:p.status = 1,因为列表页默认只展示在售商品,这个是业务逻辑兜底,不能依靠前端传参。

6.2 图片上传成功但页面访问404

图片上传功能上线后,前台商品主图一直加载失败。上传接口本身返回了成功,但浏览器访问图片URL时404。排查时发现,后端把图片保存到了项目本地磁盘的/upload目录下,但SpringBoot默认的静态资源路径是classpath:/static/,并没有把/upload映射为可访问的静态资源目录。

解决方式是在配置类里加一个资源映射,把/images/**请求映射到本地的upload目录。这里有个关键细节,开发环境和生产环境的路径不一样,不能写死绝对路径,我用配置项file.upload-path来指定,部署时可以通过启动参数动态设置。

@Configuration public class WebConfig implements WebMvcConfigurer { @Value("${file.upload-path}") private String uploadPath; @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/images/**") .addResourceLocations("file:" + uploadPath + "/"); } }

这个问题也提醒我,凡是上传类功能,一定要先确认静态资源的映射关系,再开始写上传逻辑。上传接口的成功只是第一步,能访问到才是真正的完成。

6.3 事务不生效:Service内部方法调用陷阱

订单创建功能开发完成后做了一次并发测试,结果发现库存被扣成负数还出现脏数据。排查链路比较长:先看扣减库存的Mapper方法,SQL写法没问题;再看createOrder方法上的@Transactional注解,也加了;最后用调试工具看日志,发现方法执行中没有事务日志。

原因很经典:同一个类中A方法调用B方法,B方法上的@Transactional不生效。我的createOrder方法在Service内部调用了同类的decreaseStock方法,decreaseStock上的事务注解被忽略了,整个订单创建只有外层方法的事务在起作用。解决办法有两种,一是把库存扣减方法拆到单独的Service类中,通过Spring代理调用;二是保持一个事务方法从入口开始,所有数据库操作都放在这个方法内。我选择了后者,因为订单创建是一个完整的聚合操作,本来就该在一个事务里完成,没必要拆成两个事务再合并。

6.4 前后端联调时的跨域问题

开发环境里前端跑在http://localhost:8080,后端跑在http://localhost:9090,跨域问题如期而至。浏览器的同源策略会阻止前端访问不同端口的后端接口,axios请求直接报CORS error。

我的解决方式是在SpringBoot里配置跨域过滤器。有很多人图省事会在Controller上加@CrossOrigin,但这样每个类都要加一编,我选择全局配置。

@Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.addAllowedOriginPattern("*"); config.addAllowedMethod("*"); config.addAllowedHeader("*"); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } }

这里要区分一下开发环境的跨域处理和生产环境的跨域处理。开发环境可以直接放开跨域,怎么方便怎么来;但生产环境如果前后端部署在不同的域名下,建议用Nginx反向代理来统一入口,由代理转发后端请求,而不是暴露出跨域配置。我实际部署时就是让前端请求同源的/api路径,由Nginx把/api转发到后端的9090端口,彻底规避了跨域。

6.5 前端路由刷新404问题

部署完成后又遇到一个问题:用户访问/portal/product/12这个地址正常,但刷新页面时Nginx返回404。原因是Vue Router用的History模式,前端路由是浏览器端管理的,Nginx层面没有对应的实际文件,刷新时Nginx找不到这个路径就返回404。

解决方案是让Nginx把所有非静态文件请求都重写到index.html,这样前端路由就能接管的URL。这也是Vue项目部署到Nginx时的标配配置:

location / { try_files $uri $uri/ /index.html; }

同样的问题在管理后台也存在,所以我部署时不仅要配前端路由重写,还要注意后端接口路径的/api不要被这个规则误伤。实际的Nginx配置是把location /api单独转发到后端服务,location /才走前端history回退。

7. 打包部署与基于这套系统的扩展思路

7.1 开发环境与生产环境配置分离

这个项目从开发到部署我一直坚持配置文件按环境拆开,application-dev.yml和application-prod.yml,通过启动参数--spring.profiles.active=prod来切换。开发环境的数据库连接、图片上传路径、日志级别都跟生产环境不同,混在一起迟早会出问题。最典型的就是数据库连接,开发时连本地库,生产时连服务器库,配置一旦混淆,打包后启动就会连接失败。

还有一个容易忽略的点是前端接口地址。开发时Vue通过Vite代理转发请求解决跨域,生产环境打包后我用Nginx反向代理转发/api,所以前端代码里只保留相对路径请求。如果你把前端和后端部署在不同域名下,就需要在request.js里改为绝对地址,但这会引入新的跨域问题,不如统一走代理。

7.2 Nginx部署前后端的具体步骤

部署时我把前端打包后的dist目录放到服务器上的/usr/share/nginx/html目录,后端用java -jar启动。Nginx配置的关键点有三个:监听80端口、静态文件路径指向dist、/api开头的请求转发到http://localhost:9090。这是一个非常典型的单机部署方案,很多中小项目的线上架构都是这么跑的。

完整流程记录如下:先在本地执行mvn clean package -DskipTests打包后端,npm run build打包前端;然后把后端jar包传到服务器,用nohup java -jar xxx.jar --spring.profiles.active=prod &启动;再把前端dist目录传到服务器指定目录;最后配置Nginx并nginx -s reload。整个过程半小时内能完成,难在首次配置时的细节踩坑。

7.3 基于这套系统的后续扩展方向

项目做完了,但改进空间依然很大。第一个方向是引入Redis做首页推荐商品的缓存,减少数据库压力;第二个方向是接入真实支付SDK,把模拟支付替换为微信或支付宝支付;第三个方向是增加商品的评论和评分功能,让展示端更像一个完整的电商平台;第四个方向是把图片上传迁移到云存储,避免本地磁盘容量和访问性能成为瓶颈。

如果要做成真正可交付的商业项目,我建议在代码里做一次重构,把购物车、订单、库存扣减这些核心模块抽离成独立服务。不过对现阶段来说,这套系统的意义在于让开发者完整经历从需求分析到系统上线的一个全过程,它包含的每个环节——需求拆解、数据库建模、接口设计、前端页面开发、前后端联调、部署上线——都是实际工作中真正常用的技能。

实际跑过一遍之后,我对这套SpringBoot+Vue+MyBatis+MySQL组合的认知也更深了一步。最初看各种教程时总觉得框架学习是最难的,真正动手做才发现,最花时间的永远是业务逻辑的完整性和边界情况的处理,比如库存校验、订单状态机、权限控制。框架只是工具,清晰的需求分析和严谨的工程实现才是这个项目能顺利交付的根本原因。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/13 7:12:54

华为eNSP HCIA综合实验:OSPF+VLAN+ACL+DHCP闭环工程

简介&#xff1a;本资源是面向华为HCIA认证备考者与网络初学者的eNSP综合实验实践包&#xff0c;聚焦路由交换基础、VLAN划分、RIP/OSPF动态路由配置及ACL安全策略等核心考点&#xff0c;助力理论落地与实操能力提升。压缩包共8个文件&#xff0c;含3个PC设备配置XML文件&#…

作者头像 李华
网站建设 2026/10/13 7:12:08

FileZilla Server 0.941 部署与排坑完全指南

简介&#xff1a;面向需要快速部署 FTP 服务的网络管理员与学习者&#xff0c;这是一套基于 FileZilla Server 0.941 的预配置整合包。资源已完成 user01-user56 共 56 个用户的创建&#xff0c;统一密码 123456&#xff0c;并设置所有成员共享的虚拟目录 share&#xff0c;同时…

作者头像 李华
网站建设 2026/10/13 7:12:06

NSSM 2.24实战:Python脚本Windows服务化与自动重启日志管理

简介&#xff1a;NSSM服务管理器&#xff08;Non-Sucking Service Manager&#xff09;的构建版本&#xff0c;面向系统管理员、运维人员及服务开发调试者&#xff0c;用于将任意可执行程序封装为Windows后台服务&#xff0c;解决程序需随系统启动、用户未登录也可持续运行的问…

作者头像 李华
网站建设 2026/10/13 7:11:55

Win11离线安装.NET Framework 3.5:DISM命令与sxs源指南

简介&#xff1a;.NET Framework是Windows上运行大量桌面与业务应用的基础运行时&#xff0c;而Win11默认不包含3.5版本&#xff0c;导致旧程序无法启动或报错0x800F0954。实际上&#xff0c;启用该功能并非需要一个普通安装包&#xff0c;而是要求系统从本地功能源读取组件——…

作者头像 李华
网站建设 2026/10/13 7:11:34

Win11离线安装.NET Framework 3.5:sxs源与DISM排错全指南

简介&#xff1a;一份针对Win11离线环境安装.Net Framework 3.5的实操文档&#xff0c;面向因安全策略或硬件限制无法联网、却需要运行依赖该框架软件的系统管理员与运维工程师。整个资源打包为单个docx文件&#xff0c;大小仅45KB&#xff0c;内容紧凑&#xff0c;可直接用办公…

作者头像 李华
网站建设 2026/10/13 7:11:05

Spring Boot接口版本控制实战:自定义注解实现多版本共存

其实接口版本控制这件事&#xff0c;很多团队是吃了线上事故的亏之后才开始认真对待的。你想想&#xff0c;一个对外提供服务的Spring Boot应用&#xff0c;今天接口里加个字段&#xff0c;明天有人把参数类型改了&#xff0c;下游调用方凌晨三点爬起来报警&#xff0c;这种场景…

作者头像 李华