新闻详情

新闻详情

首页 / 资讯中心 / 详情

SpringBoot+Vue+MyBatis打造电子产品销售系统:从设计到部署全解析

发布时间:2026/9/30 3:02:18来源:尧图网络
SpringBoot+Vue+MyBatis打造电子产品销售系统:从设计到部署全解析
做管理系统这套东西说难不难说简单也不简单。刚好最近在整理一套基于SpringBootVueMyBatisMySQL的电子产品销售系统源码产品信息管理、购物车、订单流转、库存扣减、后台统计该有的模块全都有前后端分离拿来就能跑。这篇文章不打算只堆截图我会把这套项目从数据库设计、后端接口拆分、Vue页面联调、到最后本地部署的完整链路拆开讲清楚把实际开发中容易踩的坑也一并说出来。如果你正在做课程设计、毕业设计或者刚进公司想找一个能快速上手的完整项目参照这篇应该能直接帮你省掉不少试错时间。1. 项目整体设计与技术选型思路1.1 这套技术栈到底解决了什么问题SpringBoot负责后端接口服务Vue负责管理后台页面MyBatis负责操作MySQL里的业务数据。这种组合在Java全栈开发里早就不是新鲜事但能长期稳坐主流位置是因为它刚好卡在“够用”和“不复杂”之间。SpringBoot本身就是一个极度简化SSM配置的框架去掉了大量XML配置内嵌Tomcat打成一个jar包就能跑。对于企业级Web系统来说SpringBoot最核心的价值不是“快”而是它把依赖管理、自动配置、健康检查这些基础设施都收编了开发者能专心写业务逻辑。加上Spring全家桶对事务、缓存、定时任务的支持做电商类管理系统非常顺手。Vue作为前端框架核心价值来自组件化。后台管理系统通常包含大量重复性极强的区块比如表格、弹窗、表单、分页条这些在Vue里都能封装成独立组件反复使用。Vue的响应式数据绑定在实现购物车增减、订单状态切换这类操作时非常自然不需要手动操作DOM只要改数据页面自己会跟着变。MyBatis在数据库访问层提供了极大的SQL自定义空间。电商销售系统总会出现多条件组合筛选、动态排序、报表统计这类查询需求MyBatis的XML动态SQL标签可以很优雅地处理这种情况。跟JPA那种“全自动ORM”相比MyBatis更像是“半自动”实体映射关系交给你控制SQL怎么写也交给你控制长期维护项目时更可控。1.2 为什么不用更“新潮”的架构每过一段时间就有人问为什么不直接上Spring Cloud为什么不把前端换成ViteTSPinia我的意见是做销售系统这种业务首要目标不是炫技而是稳定、清晰、好维护。Spring Cloud微服务适合多团队并行、亿级流量、高可用要求的业务场景。但一套电子产品销售系统用户量、商品量、订单量在绝大多数企业场景下都处于单库单表能扛住的量级硬拆微服务只会增加部署复杂性。服务拆分后每个模块之间的调用、数据一致性、日志追踪都是额外成本。用单体SpringBoot加上合理的模块分包就能把业务边界理得足够清楚。前端技术选型也是同样道理。Vue本身的生态已经很完整配上Vue Router、Pinia、Element UI这类成熟方案开发后台管理系统效率非常高。电商后台核心是表格表单权限不是花哨的交互动画Vue在这类场景下稳定性经过大量验证对新人来说上手门槛也远低于其他组合。还有一个更实际的因素这套技术栈在国内招聘市场上存量太大了。作为企业项目招人容易接手的人多知识库丰富出了问题随便一搜都有对应解决方案。这套选型不一定是技术上限最高的一套但绝对是性价比最高的一套。2. 数据库设计撑起销售系统的主干2.1 核心表结构设计一套电子产品销售系统围绕的主线很简单用户浏览电子产品加购物车提交订单商家发货用户确认收货。数据库表就该围绕这条业务链铺开。用户表一般包含账号、密码、昵称、角色、状态字段。角色字段用来区分前台用户和后台管理员状态字段用来做禁用/启用。需要注意密码绝不能明文存储使用BCrypt加密是Spring Security的默认方案也是企业项目的常规操作。商品表是这套系统的核心资产。电子产品跟衣服鞋帽不同它有固定的品牌、型号、参数规格所以商品表里除了常规的名称、价格、库存、销量、上架状态之外最好单独设计品牌和型号字段。分类表单独建一张通过parent_id实现多级分类比如“手机“下面挂”智能手机“、”拍照手机“这样前端展示分类树时很灵活。购物车表的核心字段是用户ID、商品ID、数量、勾选状态。这里有个容易被忽略的问题购物车记录的是“当时加购的快照信息”但实际下单时仍需要重新读取商品表的最新价格。原因很简单商品可能调价了、下架了直接拿购物车里的价格下单会出大问题。订单表和订单明细表是整套系统里数据关系最紧密的两张表。订单表记录订单号、用户ID、订单总金额、订单状态、收货人信息、支付时间等。订单明细表则记录每一个商品的下单数量、下单时快照价格、商品名称和ID。为什么明细表里要冗余商品名称和价格快照因为订单生成之后商家可能修改商品价格甚至删除商品如果明细不存快照历史订单的展示就会缺少数据。我整理了一张简化版的核心表结构字段按实际开发常用的标准来定义建表SQL也可以直接参考表名作用关键字段user用户与管理员username, password, role, statuscategory商品分类name, parent_id, sortproduct电子产品信息name, brand, model, price, stock, sales, statuscart临时购物车user_id, product_id, quantity, checkedorders订单主表order_no, user_id, total_amount, statusorder_item订单明细order_id, product_id, product_name, price, quantity2.2 设计上的几个关键取舍第一个取舍是金额字段一律使用DECIMAL不用FLOAT或DOUBLE。FLOAT在二进制存储下会有精度丢失商品单价、订单总额这种字段只要出现0.1元级别的误差就会引发纠纷。DECIMAL(10,2)既能满足整数部分8位、小数2位的常规业务需求也能保证精度准确。第二个取舍是业务数据不使用物理删除而是通过status字段做逻辑删除。用户可能误删购物车记录商品可能被临时下架这些操作背后的数据需要保留审计链路。如果直接DELETE后续做数据分析、库存回溯、订单合规审计时就拿不到原始数据了。比如商品下架实际是status从1改成0查询时默认只查status1的记录。第三个取舍是订单号必须全局唯一。自增ID虽然唯一但暴露了业务量也容易被遍历。常用的做法是日期时间随机数用户ID后四位拼成一个20位左右的字符串订单号比如用yyyyMMddHHmmss加6位随机数加上一个独立的Redis自增序列如果没有Redis用数据库序列或者UUID替换也是可以的。第四个取舍是外键约束尽量不用外键关系由业务层维护。很多企业级项目里都默认禁用数据库外键原因非常简单高并发写入时外键约束会带来锁竞争影响性能分库分表场景下物理外键完全失灵。对这套系统来说表与表之间的关系在Mapper层通过字段关联来维护同样能保证数据一致性。3. 后端核心模块拆解SpringBootMyBatis落地3.1 动态SQL是MyBatis最值钱的功底MyBatis最强大的地方就是XML里的动态SQL标签。销售系统最常见的商品管理场景就需要支持名称模糊搜索、分类筛选、品牌筛选、价格区间筛选、上下架状态筛选这些条件任意组合。如果全用Java代码去拼SQL代码会又臭又长还容易SQL注入。用中where配合if标签可以直接在XML里完成智能拼接。我摘一个典型的组合查询SQL写法select idselectProductList resultTypecom.example.entity.Product SELECT * FROM product where if testkeyword ! null and keyword ! AND (name LIKE CONCAT(%, #{keyword}, %) OR brand LIKE CONCAT(%, #{keyword}, %)) /if if testcategoryId ! null AND category_id #{categoryId} /if if testminPrice ! null AND price gt; #{minPrice} /if if testmaxPrice ! null AND price lt; #{maxPrice} /if if teststatus ! null AND status #{status} /if /where ORDER BY create_time DESC /select这段代码里里外外有几个关键细节想特别说明。where标签会自动处理第一组if前的AND关键字。如果没有where用户不加筛选条件时SQL就会多出一个AND报错。很多新手在初学MyBatis时都在这上面翻过车。价格比较里的gt;和lt;是XML转义符号。在XML文档里直接写大于号小于号会被解析器当成标签符号处理所以必须使用转义。对应gt;对应lt;这个坑一旦踩过就忘不掉。LIKE查询用CONCAT(%, #{keyword}, %)拼接而不是直接写%${keyword}%。${}是字符串直接替换存在SQL注入风险#{}是预编译参数占位符安全可靠。实际项目开发中凡是用户输入的内容都只能用#{}。3.2 订单提交的并发与事务处理销售系统最核心的高并发场景集中在下单。用户提交订单时后端要做的事情包括读取购物车数据、校验商品状态、计算总价、扣减库存、生成订单主表和明细表数据、清空购物车。这个过程中最容易出问题的是库存扣减。常规思路是先查询库存判断库存是否充足再执行UPDATE减库存。这个思路在并发环境下一分析就有严重漏洞两个请求同时读到库存为1都判断库存充足都执行扣减最终库存就变成负1了。正确的做法是把判断和扣减合成一条UPDATE语句通过SQL条件来保证原子性UPDATE product SET stock stock - #{quantity} WHERE id #{productId} AND stock #{quantity}这条语句的关键是AND stock #{quantity}。数据库在执行UPDATE时会对这条记录加行锁并发情况下只有一个请求能成功执行。判断MyBatis返回的影响行数如果返回1说明扣减成功如果返回0说明库存不足直接抛出异常回滚事务。把判断库存和扣减库存合并成一个原子操作能有效避免超卖。整个下单流程需要在事务中执行。在SpringBoot中只需要在Service层方法上添加Transactional注解让库存扣减、订单插入、明细插入、购物车清理任何一个环节失败都能整体回滚。事务开始的时机建议尽量后移只包裹真正操作数据库的业务代码不要在事务里去调用外部接口或者做耗时的IO操作避免长事务锁表。订单金额计算也存在一个容易出错的坑不能直接信任前端传过来的总价。前端传的数据理论上都可以被篡改正确做法是在后端根据商品ID重新读取价格再乘以数量计算订单总金额。后端服务永远只相信自己查询出来的数据在订单这种涉及资金的核心逻辑上这条原则铁打不动。3.3 配置文件里容易被忽略的细节SpringBoot项目的application.yml是整个系统的启动入口。我第一次把项目交付给别人跑的时候被问最多的几乎全是配置问题。把核心配置拆开来说一下。server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/electronics_sales?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8 mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl数据库连接串里的serverTimezoneAsia/Shanghai必须有。MySQL 8.x版本驱动对时区很敏感不配置这个参数连接时会报The server time zone value ... is unrecognized那年我碰到这个报错排查了整整半小时。driver-class-name使用com.mysql.cj.jdbc.Driver这是MySQL 8.x的驱动类名。如果项目里还在用旧版的com.mysql.jdbc.Driver驱动加载时会警告有些高版本的SpringBoot还会直接报错。MyBatis的map-underscore-to-camel-case: true允许自动把数据库里的create_time映射到实体类的createTime属性上。没有这个配置所有查询结果里带下划线的字段都会是null这又是一个经典的新手坑。log-impl配置成StdOutImpl在控制台打印SQL语句。开发环境开着很爽能看到MyBatis实际执行的SQL和参数值排查问题一目了然。生产环境建议关闭SQL日志避免敏感信息和性能损耗。4. Vue前端从环境到联调4.1 开发环境搭建的注意点Vue项目的前置环境主要就三样Node.js、npm或pnpm、Vue CLI。很多同学在这步就会卡住大多数问题是Node.js版本和项目不匹配。这个项目推荐Node.js使用16.x或18.x长期支持版本。版本太老可能导致npm安装依赖时出现各种兼容性报错版本太新又可能跟一些旧版webpack插件冲突。安装Node.js之后在命令行里执行node -v和npm -v确认版本号正常。项目安装依赖用npm install。这一步在国内环境下一旦网络慢经常出现下载超时或者依赖缺失。解决方案是设置淘宝镜像源npm config set registry https://registry.npmmirror.com这条命令设置完成后重新执行npm install速度会明显提升。我见过不少人因为依赖安装的问题直接放弃了源码项目其实很多时候只是镜像源没有配好。Vue项目启动之前还要检查有没有安装vue/cli-service。Vue CLI脚手架创建的项目启动命令是npm run serve执行之后Vite或Webpack会编项目并启一个本地开发服务器默认端口是8080。如果8080被占用了可以手动指定端口npm run serve -- --port 30004.2 接口封装与权限拦截前端开发中最重要的约定是统一管理API请求不要在每个页面里散装调用axios。我在项目里习惯创建一个request.js文件封装axios实例配置baseURL、超时时间最重要的是在请求和响应拦截器里做统一处理。import axios from axios const request axios.create({ baseURL: /api, timeout: 10000 }) // 请求拦截器携带token request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer token } return config }) // 响应拦截器统一处理错误 request.interceptors.response.use( response { const res response.data if (res.code ! 200) { alert(res.message || 请求失败) return Promise.reject(new Error(res.message)) } return res }, error { if (error.response error.response.status 401) { // 登录过期跳转登录页 router.push(/login) } return Promise.reject(error) } )这套拦截逻辑能解决两个痛点一是登录鉴权所有请求自动携带token二是接口错误提示统一处理不用每个页面单独去判断返回码。后台管理的菜单和按钮权限可以通过Vue Router的导航守卫结合用户角色来控制管理员能看到的菜单和普通操作员看到的菜单天然不一样。4.3 前后端联调常见问题联调阶段最常遇到的就是跨域问题。前后端分离后前端跑在http://localhost:3000后端跑在http://localhost:8080浏览器跨域安全策略就会拦截请求。解决跨域有两种主流方案。第一种是在后端配置CORS过滤器允许指定前端域名跨域。在后端SpringBoot项目中新建一个CorsConfig类Configuration public class CorsConfig { Bean public CorsFilter corsFilter() { CorsConfiguration config new CorsConfiguration(); config.addAllowedOrigin(http://localhost:3000); config.addAllowedMethod(*); config.addAllowedHeader(*); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, config); return new CorsFilter(source); } }第二种是前端开发服务器配置代理。在Vue项目根目录的vue.config.js中配置devServer代理module.exports { devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }这一配置的效果是前端请求/api/product/list时devServer将请求转发给http://localhost:8080/api/product/list。开发环境下代理方案更常用因为不需要动后端代码同时能保持前端看起来在请求同源地址。生产环境一般由Nginx统一配置反向代理将/api转发到后端服务思路完全一致。5. 从本地跑通到部署的完整流程5.1 快速跑通本地项目的实操步骤拿到完整源码之后很多人会迫不及待地启动项目。千万不要跳过初始化数据库这步否则后端启动后所有接口都报表不存在。数据库准备阶段先在MySQL中创建一个空库CREATE DATABASE IF NOT EXISTS electronics_sales DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;字符集必须使用utf8mb4。电子产品的商品标题、详情里会出现特殊符号比如“iPhone 15 Pro Max (A3104)”这里的圆括号、空格都很普通但某些产品标题里会出现emoji、生僻字utf8mb4才能完整存储。然后导入项目SQL脚本。我建议用命令行导入而不是在图形化工具里点来点去效率高很多mysql -uroot -p electronics_sales sql/electronics_sales.sql导入完成后可以看一眼表清单确认表数量对得上再往后走。后端部分用IDEA打开SpringBoot项目等待Maven下载依赖。这里需要确认本地Maven的镜像源配了阿里云否则下载SpringBoot相关依赖会等到怀疑人生。Maven的settings.xml里配置镜像源的原理跟npm镜像一样都是为了在依赖服务器之间架一个加速通道。等依赖下载完成修改application.yml里的数据库账号密码启动main方法。前端部分按上面说的方法安装依赖后启动服务。浏览器打开前端地址默认管理员账号一般写在SQL脚本里直接登录就能看到完整的后台界面。5.2 部署阶段的注意事项项目本地跑通只算第一步真正交付或者上线还需要过一次部署。后端打包非常简单SpringBoot直接打jar包mvn clean package -DskipTests打包完成后target目录下会生成一个xxx.jar。Linux服务器上执行nohup java -jar electronics-sales.jar --spring.profiles.activeprod app.log 21 这里nohup和的配合很重要nohup让进程忽略挂断信号让进程在后台运行。日志重定向到app.log里方便随时排查问题。前端部署一般是先执行npm run build生成dist静态文件目录再把dist目录部署到Nginx下通过Nginx反向代理把/api路径转发到后端服务的8080端口。Nginx相关配置不展开细说但核心逻辑就是让前端页面和后端接口共用同一个域名避免跨域问题。部署完成后建议立刻检查两件事一是后端启动日志里是否出现Started Application in xxxx seconds二是前端页面能否正常登录并加载商品列表。如果接口超时多半是服务器安全组没放开端口或者Nginx代理配置了不对的地址。6. 常见问题与排查技巧实录6.1 典型报错与解决对照表这些年在调试销售系统项目时遇到的报错来来回回其实就那么几类。我整理了一张对照表每个问题后面附上解决思路建议直接收藏。报错现象根本原因解决方案数据库连接超时/拒绝连接MySQL服务没有启动或端口不对确认netstat -anThe server time zone value is unrecognized连接串缺少时区配置url增加serverTimezoneAsia/ShanghaiMyBatis查询结果全是null实体属性与列名映射失败开启map-underscore-to-camel-case: true前端请求接口报CORS错误前后端跨域没解决后端配置CorsFilter或前端配置devServer代理商品图片不显示图片路径用了绝对本地地址图片使用相对路径存储配合Nginx静态资源映射页面白屏/路由404history模式路由在Nginx下未配置重写Nginx配置try_files $uri $uri/ /index.html库存偶尔扣超先查询再扣减的代码逻辑改成UPDATE ... WHERE stock #{quantity}原子扣减SpringBoot启动报端口被占用8080被其他进程占用换端口或lsof -i:8080查PID后结束进程最后一个端口占用问题我第一次在Windows上部署时就遇到过。IDEA里明明没启动服务去访问8080提示端口被占用后来用命令行查出来是残留的Java进程没退出。在Windows下用netstat -ano找到占用端口的PID再用taskkill /F /PID 进程号解决即可。6.2 几句掏心窝的提示关于MyBatis缓存很多人一上来就想开二级缓存我的建议是默认不开。这个项目里的商品、分类、订单数据变更频繁缓存一旦出现脏数据用户看到的价格和库存就不一致。MyBatis一级缓存作用范围默认在SqlSession生命周期内SpringBoot集成后每次请求都会新建SqlSession所以实际开发中等同于没有缓存。如果页面访问量确实大优先在Service层做本地缓存或引入Redis不要在Mapper层用二级缓存给自己挖坑。关于SQL日志开发环境打开MyBatis日志打印之后你会非常直观地看到每个请求对应的SQL语句和参数。排查问题的效率会提升一截。比如前端传了一个为空的条件你一看日志发现SQL里根本没有这个条件就能迅速定位是前端没传参还是Mapper动态SQL写错。关于这个项目后续还能怎么扩展我个人觉得加一个Redis做购物车缓存和token管理是最自然的下一步。SpringBoot提供Spring Data Redis整合起来并不复杂。购物车不落库、订单库存热点数据走缓存性能会有肉眼可见的提升。最后再分享一个小技巧如果前端页面报错别急着看代码先把浏览器F12的Network面板打开看接口返回状态。后端接口返回400是参数问题500是代码异常401是权限失效404是路径写错。拿到状态码再定位问题会比在页面里瞎猜快十倍。这套项目整体做完从数据库建模到前后端功能联调基本上把企业级Web系统开发的整个闭环走了一遍。我自己在整理源码的过程中最大的体会还是那句话技术栈只是工具真正值钱的是对业务逻辑的理解和对数据一致性的把控。希望这篇文章能帮你把项目跑起来也能让你在写代码的时候心里多几分底气。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

鉴相器相位提取:arctan与arctan2的工程区别与选择指南 2026/9/30 3:59:49

鉴相器相位提取:arctan与arctan2的工程区别与选择指南

最近在调试一个X波段的FMCW雷达前端,数字下变频之后用反正切函数提相位,结果在目标跨过零频的瞬间,相位曲线出现了离谱的跳变,数据直接没法看。排查了一晚上,最后定位到问题根源:我用的是一元反正切atan(Q/…

阅读更多 →
SpringBoot学生公寓管理系统源码拆解:从表结构到部署排错全攻略 2026/9/30 3:59:49

SpringBoot学生公寓管理系统源码拆解:从表结构到部署排错全攻略

学生公寓管理系统算得上Java Web里最经典的一类练手项目,需求清晰、角色分明、业务闭环完整,特别适合毕业设计和课程设计。配合SpringBoot来写,开发效率比过去SSH时代高出一大截,一个学生从零搭起来也就两三周的事。这个“springb…

阅读更多 →
Thrift跨语言RPC实战:从IDL设计到服务端选型与踩坑记录 2026/9/30 3:59:49

Thrift跨语言RPC实战:从IDL设计到服务端选型与踩坑记录

你们有没有遇到过这种场景:一个服务端用 Java 写得好好的,客户端突然来了个 Python 脚本要对接,后来又冒出个 Go 服务要调同一个接口。刚开始还能靠 RESTful 接口硬扛,JSON 来 JSON 去也能跑,可一旦接口字段多起来、调…

阅读更多 →
Java集合框架性能优化实战:从选型到工具类的正确姿势 2026/9/30 3:59:49

Java集合框架性能优化实战:从选型到工具类的正确姿势

前几天同事来找我看一个线上接口的耗时问题,逻辑明明不复杂,可数据量一上来,响应时间就从几十毫秒直接跳到几秒。我把代码翻了一遍,发现他在循环里用 LinkedList 频繁 get 索引、HashMap 没给初始容量、还顺手在遍历的时候用集合的…

阅读更多 →
输电线路销钉小目标检测实战:从数据校准到Jetson端部署 2026/9/30 3:59:48

输电线路销钉小目标检测实战:从数据校准到Jetson端部署

简介:本资源是一篇面向电力系统运维工程师、计算机视觉算法研究者及深度学习实践者的学术型技术文档,聚焦无人机巡检场景下输电线路销钉脱落故障的智能识别问题。文章提出基于Faster R-CNN的目标检测方案,系统对比ACFAdaboost、HoughLSD等传统…

阅读更多 →
PHD数据抓取实战:从接口分析到RPA与断点校验 2026/9/30 3:59:35

PHD数据抓取实战:从接口分析到RPA与断点校验

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉