news 2026/9/28 5:57:25

SpringBoot+Vue植物销售管理系统实战:从订单库存到权限部署全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot+Vue植物销售管理系统实战:从订单库存到权限部署全解析

做植物电商项目时,我一开始想的很简单:SpringBoot写接口,Vue画页面,前后端一拼就完事。等真正把“植物销售管理系统”从零写完,我才意识到这类系统真正考验人的地方,根本不在CRUD,而在订单状态流转、库存扣减、权限控制、图片资源管理这些容易出幺蛾子的细节上。这篇文章把我完整的设计思路、选型理由、表结构、核心代码和踩坑记录都铺开讲,从环境搭建到部署上线一整套流程都有,适合正在做类似毕设或练习项目的人参考,也适合想快速上手SpringBoot+Vue前后端分离开发的朋友。

1. 需求拆解:一套植物销售管理系统到底要管什么

1.1 从线下花店场景推导出来的核心需求

做系统最忌讳一上来就建表写代码。我习惯先把自己代入线下花店老板的角色,把日常经营动作全部列出来。

线下的花店每天要做什么:进货、把植物摆上货架、给植物标价、顾客来店挑选、结账、打包带走、偶尔处理退货。如果把这个场景搬到线上,就会一一对应出以下功能:后台录入和维护植物商品信息、设置上架和下架状态、商品分类、顾客浏览商品列表和详情、加入购物车、提交订单、模拟支付、管理收货地址、查看订单状态,以及后台的订单处理、发货、统计销售数据。

这个系统里最核心的实体有四个:用户、植物商品、购物车、订单。围绕这四个实体,功能模块其实非常清晰,不需要堆叠复杂的业务。很多初学者做这类项目容易陷入一个误区,就是功能越加越多,什么优惠券、秒杀、积分商城全往上套,结果每个功能都做得半生不熟。我的建议是先把基础链路打通,也就是“商品列表—商品详情—购物车—确认订单—后台发货”这条完整闭环,再考虑锦上添花。

1.2 三种角色与权限边界划分

植物销售管理系统里,角色划分是整个权限设计的起点。我最终把系统分为三种角色:普通用户、管理员、超级管理员。普通用户只能操作前台商城相关的功能,管理员可以进入后台进行商品管理、订单处理和用户管理,超级管理员额外拥有管理员账号分配和数据统计权限。

权限边界划清楚之后,后端的接口设计也就有依据了。用户相关的接口走用户鉴权,管理员相关的接口单独加一层权限校验。如果所有接口对所有人开放,后台管理系统就等于裸奔,只要有人猜到路径就能篡改数据,这是一个真实系统绝对不能接受的。

我在实现时用JWT保存登录状态,JWT里写入用户ID和角色,后端拦截器解析token后把用户信息放入请求上下文。进入管理员接口时,再从上下文取出角色判断是否是管理员。这套方案简单可靠,完全够一个中小型电商系统使用。

1.3 功能模块清单与开发优先级

功能模块规划成两大部分:前台商城和后台管理。

前台商城包含:用户注册登录、植物分类展示、商品列表(支持关键词搜索)、商品详情、加入购物车、修改购物车数量、提交订单、订单列表与详情、取消订单、个人中心。

后台管理包含:管理员登录、商品管理(新增、编辑、上下架、删除)、分类管理、订单管理(查看订单、发货、完成)、用户管理、统计概览。

开发优先级上,我强烈建议按照“登录鉴权—商品管理—商品展示—购物车—订单—后台管理”这个顺序推进。因为商品是整个交易链条的起点,商品没有做出来,购物车和订单都是空中楼阁。登录鉴权提前做是因为后续所有接口联调都依赖用户身份,如果放到最后统一补,会来回改代码改到怀疑人生。

2. 技术选型与项目初始化:为什么是SpringBoot和Vue

2.1 前后端分离带来的好处和代价

选SpringBoot加Vue这套组合,说白了就是奔着前后端分离去的。前后端分离之后,后端只提供RESTful API,不用再关心页面长什么样;前端只专心做页面交互和渲染,通过Ajax请求拿数据。开发时可以并行推进,前端用Mock数据先画页面,后端用Postman先测接口,最后联调时对接口字段就行。

但前后端分离不是没有代价。最直接的代价是跨域问题,前端跑在8080端口,后端跑在9090端口,浏览器会拦截非同源的Ajax请求,必须通过CORS配置解决。另一个代价是部署更复杂,过去一个Tomcat能搞定的事,现在要分别部署前端静态文件和后端服务。

这些代价在实际开发中都是可控的,尤其是CORS问题,后端加一个配置类就能解决。真正让开发体验提升的是分工明确,后端不用再跟Thymeleaf模板语法较劲,前端也不用在JS里拼HTML字符串,两边都可以用自己最顺手的工具链。

2.2 SpringBoot版本选择与自动装配机制

创建项目时第一关就是版本选择。我使用的是SpringBoot 2.7.18,原因非常现实:3.x版本基于Jakarta命名空间,很多老教程和老依赖的导入方式都变了,如果跟着网上教程走容易卡壳。2.7.x是2.x系列的最终版本,稳定、资料多、兼容性好,做毕设和中小型项目完全够用。

这里顺带说一下SpringBoot最核心的“自动装配”机制。为什么引入一个spring-boot-starter-web依赖后,不需要配置Tomcat就能启动Web服务?因为SpringBoot在spring.factories或AutoConfiguration.imports文件里注册了一堆自动配置类,启动时会根据当前classpath中是否存在对应类来决定是否生效。比如classpath里有SpringMVC相关的类,DispatcherServletAutoConfiguration就会自动帮你配置好DispatcherServlet。

理解自动装配对我排错帮助很大。有次我的项目启动后访问接口一直404,排查了半天,最后发现是启动类的位置放错了。SpringBoot默认扫描启动类所在包及其子包,而我的Controller放在了启动类的兄弟包里,自然扫描不到。这类问题不看自动装配原理根本想不到。

2.3 用IDEA快速搭建后端工程

现在创建SpringBoot项目非常方便,我用的方式是IDEA自带Spring Initializr。新建项目时选择Spring Boot版本,勾选Web、MySQL Driver、MyBatis Framework、Lombok这几个依赖,IDEA就会自动生成一个可直接运行的空工程。这里有个经验:Lombok一定要勾上,它能用注解省略掉实体类的getter/setter,代码量直接减少一半。

项目目录我习惯按功能分包,而不是按技术分层。也就是说,我不建controller包、service包、mapper包这种技术维度目录,而是建user包、product包、cart包、order包,每个包下面再放controller、service、mapper、entity。这种分包方式在项目变大之后优势非常明显,改动一个功能时所有相关文件都在同一个包下,不用来回切换目录。

启动类上我加了@MapperScan注解扫描Mapper接口,这样每个Mapper接口上就不用再单独标@Mapper。如果忘记这个注解,MyBatis会提示找不到实现类,启动直接报错。这个坑我在早期项目里踩过不止一次。

2.4 Vue3 + Vite环境配置与开发调试工具

前端工程我用Vue3加Vite创建,命令是npm create vue@latest,Vite的冷启动速度比Webpack快一个量级,开发体验好了很多。创建过程中会询问是否启用TypeScript、Vue Router、Pinia、ESLint这些特性,我建议全选Yes,省得后面手动补装依赖。

环境配置里最容易出问题的是npm镜像源,国内直接npm install经常卡住或者报网络错误,需要先执行npm config set registry https://registry.npmmirror.com。装完依赖后,我会顺手安装vue-devtools浏览器插件,调试Vue组件状态时离不开它。在Chrome扩展商店安装后,打开Vue项目页面,控制台会出现Components和Vuex/Pinia两个标签页,组件树的props、ref、computed状态一目了然,排查页面数据不更新的问题非常高效。

3. 数据库设计:做好这几张表,系统就成功了一半

3.1 植物商品表的特殊字段设计

商品表是整个系统数据设计的核心。植物商品和普通商品最大的区别在于它有一个“分类层级”概念,比如“绿植—观叶植物—龟背竹”,所以我用parent_id字段做自关联分类表,支持无限层级扩展。

商品主表我定义的字段包括:id、category_id、name、subtitle、main_image、detail_images、price、stock、sales、status、description、created_time、updated_time。其中price字段用decimal(10,2),避免float的精度问题。detail_images用JSON字符串存储多张图片URL,查询时用fastjson或Jackson解析成List,不要为多图单独建一张表,那种设计在这个场景下属于过度设计。

有一个字段容易被忽略但非常重要,就是sales销量字段。购物车和商品列表页面要把商品按销量排序,如果每次排序都去统计订单明细表,数据量大了会非常慢。直接在商品表维护一个冗余销量字段,每次支付成功后加一,排序时直接order by sales,高效得多。

3.2 购物车、订单与订单项的关系

购物车表设计相对简单,字段是id、user_id、plant_id、quantity、checked。这里我用user_id加plant_id做唯一索引,同一个用户对同一件商品只会有一条购物车记录,重复加入时直接累加数量。如果不用唯一索引,购物车会出现同一商品的多条记录,前端渲染时还要合并,白白增加复杂度。

订单相关的表是订单主表和订单明细表,这是典型的一对多关系。订单主表orders存一次下单的整体信息,包括order_no、user_id、total_amount、status、receiver_name、receiver_phone、receiver_address、remark、create_time、pay_time;订单明细表order_item存每一个商品项,包括order_id、plant_id、plant_name、plant_image、price、quantity。

订单明细表里为什么要把商品名称、图片、价格全部冗余一份?因为商品信息是可以修改的,如果顾客下单后商品改名或者价格调整,订单详情里的信息也会跟着变,会产生非常大的历史数据混乱问题。订单明细表里保存下单那一刻的快照,这是电商系统的通用做法,虽然冗余但是正确。

3.3 用户表与密码安全性设计

用户表字段是id、username、password、nickname、phone、avatar、role、status、created_time。role字段用tinyint,0表示普通用户,1表示管理员。这个系统角色数量少,用int字段比建角色表加关联表简单得多。

密码一定不能明文存储,我用的是BCrypt加密,SpringSecurity框架中常用的PasswordEncoder实现类,同一个密码每次加密后的结果都不同,因为每次会生成随机的盐。数据库里保存的是加密后的字符串,即使数据库泄露,攻击者拿到密文也很难反推出明文密码。实现上没有引整个SpringSecurity,只引入了spring-security-crypto这个轻量依赖,注册时encode,登录时matches比对,非常干净。

3.4 索引、时区和字符集三个比较容易翻车的地方

索引方面,我建立了这些索引:商品表的category_id索引,订单表的user_id索引,订单明细表的order_id索引,购物车表的user_id索引。这些都是高频查询条件,加上索引后查询性能有明显提升。这里有一个需要明确的点:如果一个表的数据量只有几百条,加不加索引感觉不出来,但索引是习惯问题,等数据量上来了再补就晚了。

数据库连接URL上必须加上时区和字符集参数,我最常用的连接串是jdbc:mysql://localhost:3306/plant_shop?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai。不加serverTimezone参数,高版本MySQL驱动会直接报错,因为驱动不知道你的时区是什么。不加characterEncoding,插入中文会出现乱码,这个坑特别隐蔽,因为本地开发环境可能正常,部署到Linux服务器上就乱码了。

4. 后端核心实现:从登录鉴权到订单扣库存

4.1 统一返回结构与全局异常处理

前后端联调最怕的就是接口返回格式不统一。有的接口返回字符串,有的返回JSON,有的成功和失败结构都不一样,前端axios封装里就得写一堆分支判断。我做的第一件事就是定义统一返回类Result,结构是code、message、data三个字段。code为200表示成功,code为500表示业务异常,code为401表示未登录或token失效。

每个接口直接返回Result.success(data)或者Result.error("具体错误信息"),前端拿到响应后先判断code,再决定业务处理逻辑。为了进一步减少重复代码,我加了@RestControllerAdvice全局异常处理器,Controller里任何未捕获的异常都会走到这个处理器,被转换成统一格式返回。这样既避免了异常堆栈直接暴露给前端,也保证了所有接口响应格式一致。

4.2 JWT登录鉴权与拦截器实现

登录接口的逻辑是:根据用户名查出用户,用BCrypt匹配密码,匹配成功则生成JWT返回给前端。JWT里我只放userId、username、role三个关键信息,过期时间设置成24小时。生成JWT用的库是jjwt,依赖引入简单,API也直观。

前端拿到token后存储在本地,之后每次请求都在请求头Authorization字段带上这个token。后端用一个拦截器继承HandlerInterceptor,在preHandle方法里解析token,解析失败直接返回401状态并写出JSON提示信息,解析成功就把userId和role放入ThreadLocal。这样Controller里需要当前用户信息时,直接从ThreadLocal取,不用每个接口都重复解析token。

这个方案有几个细节值得注意。拦截器中放行的路径是登录接口、注册接口、商品查询接口、商品图片路径,其他接口全部拦截。管理员专用接口除了登录态校验,还要从ThreadLocal取出角色再校验一次role是否为1,普通用户即便登录了也进不了后台接口。整个鉴权链路非常清晰,排查问题时也很直观。

4.3 商品图片上传与静态资源映射

商品图片上传是一个容易被忽略却又很影响体验的功能。我在后端实现了一个文件上传接口,接收MultipartFile参数,保存到服务器的uploads目录下,文件名用UUID加原始扩展名生成,避免中文名和重名问题。上传成功后返回可访问的图片URL,前端直接用这个URL渲染图片。

图片保存到本地后,SpringBoot默认无法直接通过URL访问uploads目录里的文件,需要实现WebMvcConfigurer接口,重写addResourceHandlers方法,将/images/**路径映射到本地的uploads目录。配置完这个映射后,前端访问http://localhost:9090/images/xxx.jpg就能直接拿到图片。

如果是部署到云服务器,我更建议把图片存储换成MinIO等对象存储服务。MinIO可以在本地搭建一个兼容S3协议的对象存储,图片上传和访问都走HTTP接口,不需要再维护本地目录权限、备份等脏活累活。SpringBoot集成MinIO也不复杂,引入minio依赖,配置endpoint、accessKey、secretKey三个参数就能用。

4.4 提交订单与扣减库存的事务控制

提交订单是整个系统逻辑最复杂的接口。它的操作步骤包括:校验购物车选中商品、计算总金额、创建订单主记录、批量创建订单明细、清空购物车、扣减商品库存、增加商品销量。这么多步骤如果某一步出错,前面的操作就会留下脏数据,所以整个方法必须加上@Transactional事务注解。

事务注解只是第一步,防止并发超卖还需要更精细的控制。如果两个用户同时下单最后一件库存,两个请求都先查到库存为1,然后都执行库存减一,库存最终变成了负数,这就是典型的超卖问题。我的解决方案是使用乐观锁方案:UPDATE语句中带条件WHERE id = ? AND stock >= ?,如果影响行数为0,说明库存不够,抛出业务异常回滚事务。这个方案效果很好,代码就一条更新语句,性能也没有额外负担。

订单号的生成也值得一提。我用的格式是时间戳加三位随机数,yyyyMMddHHmmss加上随机数拼成20位以内的字符串。订单号的唯一性直接决定了下单接口的幂等性,如果两个请求生成了相同订单号,就会出重复订单。为了保险,订单表对order_no字段加了唯一索引,即使极端情况下生成了重复号,数据库层面也会拒绝插入。

4.5 MyBatis-Plus分页查询与多条搜索条件组合

商品列表接口需要支持分页、分类筛选、关键词搜索、价格排序,这些条件组合在一起非常容易把SQL写乱。我用的MyBatis-Plus自带分页插件,配置一个MybatisPlusInterceptor Bean,添加PaginationInnerInterceptor,然后调用selectPage方法就能自动分页。

具体实现上,我用LambdaQueryWrapper构造查询条件。如果前端传入categoryId,就eq(Plant::getCategoryId, categoryId);如果传入了keyword,就like(Plant::getName, keyword);如果传入了排序方式,就用orderByDesc(Plant::getSales)或orderByAsc(Plant::getPrice)。整个查询条件都是动态拼装,前端传什么参数就加什么条件,不需要为每个接口单独写SQL。

分页插件有一个不算坑但容易忽略的细节:分页插件参数和MyBatis-Plus的版本需要匹配。如果分页插件配置了但接口不生效,返回的total总是0,大概率是插件初始化方式不对,没有把PaginationInnerInterceptor加到MybatisPlusInterceptor里,而是单独配置了一个Bean。这个错误在控制台的SQL日志里看着正常,但总页数就是不对,排查起来还挺费时间。

5. 前端实现:商城页面与后台管理的完整交互流程

5.1 项目目录结构与路由设计

前端工程我严格区分了用户端和后台管理端的代码。src/views目录下分别有user和admin两个子目录。用户端页面放在user下,包括Home、ProductList、ProductDetail、Cart、OrderList、Login等;后台管理页面放在admin下,包括AdminLayout、AdminProduct、AdminCategory、AdminOrder、AdminDashboard等。

路由设计上,我使用Vue Router的懒加载方式,每个页面组件都用import()动态导入,这样首屏只加载首页相关的代码,进入后台时才加载后台相关代码,打包体积明显减小。路由还需要区分是否需要登录权限,我封装了一个全局前置守卫,在beforeEach中判断目标路由的meta.requiresAuth属性,为true时检查Pinia中是否存储了token,没有token就跳转登录页并带上redirect参数。

后台管理的所有页面都挂在一个AdminLayout组件下,左侧是菜单栏,右侧是内容区,顶部是管理员信息。这种经典布局的好处是菜单和内容区可以独立刷新,路由变化时只有右侧内容区重新渲染,菜单的选中状态不会丢失。

5.2 axios封装:请求拦截器和响应拦截器各司其职

axios如果不封装,每个页面都写一遍请求逻辑,代码会重复到令人崩溃。我单独创建了utils/request.js文件,封装一个axios实例,统一设置baseURL和超时时间。

请求拦截器里做两件事:从Pinia或localStorage中取出token,如果有就添加到请求头Authorization;设置请求头Content-Type为application/json。响应拦截器里统一处理后端返回的数据结构,先判断HTTP状态码,200以内的正常响应再判断业务状态码code。如果code是401,说明token失效,清除本地登录信息并跳转登录页;如果code是500,直接用Element Plus的Message组件弹出业务错误提示。

有了这个封装,页面里写请求就非常清爽。比如调用登录接口,只需要const res = await loginApi(data),然后直接用res.data里的业务数据,不需要每个页面都重复写错误处理逻辑。从代码整洁度来说,值得把拦截器做得稍微丰满一点。

5.3 商品列表与购物车交互的实现细节

商品列表页我使用了Element Plus的Card组件做栅格布局,每一行显示四个商品卡片。卡片上展示商品图片、名称、价格和销量,点击卡片可跳转详情页。搜索和筛选区提供了关键词输入框、分类下拉框、排序选择器,数据变化时重新调用分页查询接口,同时保持分页组件的当前页参数。

购物车页面相对简单,核心是复选框选中状态和结算金额联动。我将购物车的数据放在Pinia的cart模块中,页面组件只负责渲染和调action方法,不直接改状态。当用户修改某个商品的选中状态或数量时,调用store中的方法更新状态,并立即调用后端接口同步购物车数据库记录。这里前端状态和后端数据的一致性是最容易出bug的地方,我采用的做法是先更新后端,成功后再更新Pinia状态,这样即使接口失败,页面状态也不会被污染。

5.4 订单流程的前端状态处理

订单状态字段我用数字表示,前端用过滤器或计算属性把数字映射成对应的中文文本。0待支付、1已支付、2已发货、3已完成、4已取消。这五个状态在订单列表中通过tag标签展示,不同状态用不同颜色,用户一眼就能看出当前订单进行到哪一步。

确认订单页面展示收货人信息、商品明细、金额明细,点击提交订单后调用后端下单接口。后端返回订单ID后,前端跳转到支付模拟页面,使用一个二维码占位图营造支付场景,点击“模拟支付成功”按钮调用支付接口,把订单状态从待支付改成已支付。整个流程用对话形式串联起来,非常接近真实电商的购买体验,但不需要接入真实支付网关,适合练习和演示。

5.5 前端调试工具与组件化开发经验

开发过程中我全程开着Vue DevTools调试。排查组件数据不更新问题时,在DevTools里选中组件,看props是否按预期传入、Pinia里的state变化时机是否对。有一次商品列表一直不刷新,后端接口明明返回了新数据,页面却还是旧状态,我通过DevTools发现是组件的响应式数据指向了同一个对象引用,修改时没有触发重新渲染,换成重新赋值方式后问题解决。这类问题不看DevTools,光靠console.log会浪费很多时间。

组件化开发方面,我抽了几个通用组件:商品卡片组件ProductCard、分页组件PaginationBar、图片上传组件ImageUpload、状态标签组件StatusTag。商品列表页和后台商品管理页都复用ProductCard和PaginationBar,只是数据来源不同。这既减少了重复代码,也保证了页面展示风格统一,后期改样式只需改一处。

6. 部署上线与高频问题排查实录

6.1 前后端打包部署的两种方式

项目做完之后要部署上线,这一步对很多不熟悉运维的人来说是个坎。我先说最传统的部署方式:后端用Maven打包成可执行jar包,上传到服务器执行java -jar plant-shop.jar,前端执行npm run build生成dist目录,把dist目录上传到Nginx的html目录下,Nginx监听80端口。

第二种方式是通过Docker部署。我给后端写了一个Dockerfile,基础镜像选择eclipse-temurin:8-jdk,把jar包拷贝进去,暴露9090端口,启动命令设为java -jar。前端同样用Nginx镜像做静态服务器,把dist目录挂载到容器里。docker-compose.yml文件里同时定义了MySQL、后端服务、前端Nginx三个服务,MySQL使用数据卷持久化数据,后端连接MySQL的地址直接填服务名即可,容器网络内部自动解析。

这里提醒一句,生产环境一定不要再用application.yml里的开发配置,我会专门建一个application-prod.yml,把数据库地址、用户名、密码、日志级别这些参数单独配置。启动时通过--spring.profiles.active=prod参数切换,这样一个项目既能本地调试又能上生产环境。

6.2 Nginx反向代理与跨域配置

前端部署后,浏览器直接访问前端域名,而Ajax请求需要访问后端接口。传统做法是直接在Nginx里配置反向代理,把所有以/api开头的请求转发到后端的9090端口。这样前端页面和后端接口都通过同一个域名访问,浏览器里不存在跨域问题,后端的CORS配置甚至可以去掉。

Nginx里关键配法是location块。location /api/ { proxy_pass http://127.0.0.1:9090/; },这里有一个非常多见的坑:proxy_pass后面的URL如果带路径且有结尾斜杠,会把location前缀替换掉;如果不带斜杠,则会把完整的/api路径带上。比如前端请求/api/user/login,如果proxy_pass配成http://127.0.0.1:9090/,后端接收到的是/user/login,这要求后端接口路径不带api前缀;如果proxy_pass配成http://127.0.0.1:9090,后端接收到的是/api/user/login,后端接口就必须以/api开头。我建议统一约定,后端所有接口路径都带/api前缀,前端axios的baseURL写成/api,Nginx里就配成带结尾斜杠的写法。

Vue项目如果开启了history路由模式,刷新页面会出现404,这是因为Nginx默认找不到对应的物理文件。解决办法是在Nginx配置里加上try_files指令:try_files $uri $uri/ /index.html;,所有不存在的路径都回退到index.html,由前端路由接管页面渲染。

6.3 实际运行中遇到的五个高频问题

第一个是端口占用。有次重启后端服务发现端口被占用,报错信息里明确提示Address already in use。我直接用lsof -i:9090查占用线程,确认是上次启动的java进程没有退出,kill掉之后重启恢复正常。Windows系统上对应的命令是netstat -ano | findstr 9090,之后taskkill进程。

第二个是数据库连接失败。报Communications link failure错误,排查了服务器防火墙、MySQL是否启动、账号密码是否配对,最后发现是application-prod.yml里数据库地址写的是localhost,而Java服务运行在Docker容器内,localhost指向容器自己而不是宿主机。改成宿主机IP或者Docker Compose服务名后问题解决。

第三个是前端白屏。build之后dist目录里的index.html直接打开是一片空白,控制台提示JavaScript加载失败。原因是路由使用了history模式,而静态文件的加载路径写成了绝对路径。解决方法是修改Vite配置的base参数,默认是/,部署到子目录时必须改成相对路径或子目录路径。

第四个是上传的图片无法显示。后端接口返回了图片URL,前端能请求到但返回404。排查后发现在Linux服务器上上传目录的路径是相对路径,而服务的工作目录和我预期的不同。更稳妥的做法是在配置里使用绝对路径指定上传目录,并且在配置类中确认目录存在,不存在就创建。

第五个是MyBatis-Plus分页插件配置了但分页不生效。控制台打印的SQL里没有LIMIT语句,total始终为0。最后发现是我把PaginationInnerInterceptor挂在了别名Bean上,上下文的拦截器链里没有它,改成像前文说的那样放进MybatisPlusInterceptor实例后一切正常。

6.4 项目后续可以怎么扩展

这个系统做完之后,我梳理了几个非常自然的扩展方向。第一个是接入真实的微信支付或支付宝支付,把模拟支付替换成真实支付回调逻辑,这会让项目更完整,但需要申请商户号,流程比较长。第二个是加入商品评价功能,用户交易完成后可以给商品打分和评论,在商品详情页展示评价列表,这能丰富商品页信息。第三个是统计分析模块,后台图表展示每日销售额、热销商品排名、用户增长趋势,可以用ECharts在前端绘制可视化图表,数据接口用SQL聚合查询实现。

最后说点我的体会。做这类管理系统的过程,技术本身并不是最大的门槛,真正有价值的是把业务逻辑梳理清楚,把方案之间的取舍想明白。比如为什么订单明细要存快照,为什么扣库存要用条件更新,为什么前后端要分离部署,这些决策在写代码之前就已经确定了。希望这篇文章能帮正打算做类似项目的朋友少走一些弯路,不管是课程设计、毕业设计还是工作中接到的真实需求,把基础链路打通、把细节问题处理干净,项目就成功了一大半。

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

普陀集团网站建设避坑:3种方案拆解性能优化与真实报价

普陀集团网站建设避坑:3种方案拆解性能优化与真实报价 网站后台突然弹出一堆乱码广告,或者首页被替换成赌博链接,这种“被黑挂马”的噩梦,很多普陀本地企业主都经历过。别慌,这通常不是你的代码写得烂,而是安全基线没做好,加上服务器配置拖累了 性能优化 ,导致攻击者有机可乘。…

作者头像 李华
网站建设 2026/9/28 5:57:16

北京网站建设公司兴田德润实惠揭秘建站完整流程

北京网站建设公司兴田德润实惠揭秘建站完整流程 网站被黑挂马,后台登录不了,页面弹出赌博广告,这是很多甲方最崩溃的时刻。别慌,先别急着删库重装,冷静下来按步骤排查才是正道。很多老板找到北京网站建设公司兴田德润实惠,第一句话就是问怎么补救,但真正值钱的是他们给出的建站完整流程,从源头避免这种惨剧。…

作者头像 李华
网站建设 2026/9/28 5:57:12

栅格化系统制作网页界面设计新手入门:一文搞懂布局痛点

栅格化系统制作网页界面设计新手入门:一文搞懂布局痛点 网站做好了没人访问,很多时候不是内容不行,而是页面乱得像一锅粥,用户停留不过三秒就关掉。想彻底解决这问题,得从底层逻辑入手, 一文搞懂…

作者头像 李华
网站建设 2026/9/28 5:56:43

WordPress突然全站404排查指南:新手建站哪家靠谱看这里

WordPress突然全站404排查指南:新手建站哪家靠谱看这里 自己不会代码,想做个网站展示业务,结果网站突然打不开,显示404?这种绝望感,河北不少做实体生意、开网店的朋友都经历过。别慌,这通常不是网站“死”了,而是配置出了岔子。…

作者头像 李华
网站建设 2026/9/28 5:56:06

做adsense对网站有什么要求?揭秘建站报价背后的流量真相

做adsense对网站有什么要求?揭秘建站报价背后的流量真相 网站做好了没人访问,这是多少运营人半夜盯着后台数据时的绝望心声。你花了钱找外包,对方报价几千块,说得天花乱坠,结果上线三个月,流量图线平得像心电图停止。这时候你才反应过来,当初在对比 建站报价…

作者头像 李华
网站建设 2026/9/28 5:55:52

QT Creator下QSerialPort串口开发实战:安装配置与高频问题排查

各位同行,今天想把QT Creator下QSerialPort串口开发这块儿捋一遍。说实话,串口开发本身不复杂,但“模块装不上”“readyRead不触发”“数据莫名其妙丢字节”这些问题,几乎每个做上位机的人都会撞上。尤其是QT Creator跨平台工程&a…

作者头像 李华