首页 / 资讯中心 / 文章详情

SpringBoot整合SSM实现超市果蔬销售商城管理系统

SpringBoot整合SSM实现超市果蔬销售商城管理系统 ★ FEATURED ARTICLE
做这种“超市果蔬销售商城管理系统”已经是这两年 Java 后端练手项目里最常见的需求之一。标题里的springboot_ssm880看着像随机编号其实就是某个课程设计项目库里的索引号核心技术栈说的是 SpringBoot 整合 SSMSpring SpringMVC MyBatis。这类项目最典型的目标就三件事前台把水果蔬菜卖出去后台把商品订单管起来管理者能看明白每天卖了什么、库存还剩多少。适合参考的人我总结下来有三类第一次做商城类毕业设计的学生、刚入行想完整走一遍 Web 全栈流程的开发者以及手头有老 SSM 项目想迁移到 SpringBoot 的人。我去年按这个思路完整做过一套今天把整条线拆开讲包括数据库设计、核心业务逻辑、踩过的坑和部署注意事项。1. 这类商城管理系统的第一眼整体设计与技术选型1.1 标题里藏着哪些信息很多人拿到题目第一反应是“这算什么技术方向”。springboot_ssm880拆开看springboot是基础框架ssm是 Spring、SpringMVC、MyBatis 的合称880大概率是项目编号或者教学视频里的集数编号。超市果蔬销售商城管理系统这几个字才是真正决定业务设计的核心超市意味着商品品类要多、价格要日常、分类要直观果蔬意味着商品有时令性、生鲜属性库存管理比普通百货更需要留神销售商城意味着有完整的用户操作链路管理系统则点明后台要有商品、订单、用户、统计的管理界面。这一个标题实际上已经把系统角色、业务范围、核心模块都圈定了后面所有设计都是围绕这几个关键词展开的。1.2 为什么是 SpringBoot SSM而不是纯 SSM我理解不少初学者会问既然都叫 SSM为什么还要套一层 SpringBoot这个问题值得说清楚。纯 SSM 项目需要手动配置 Spring 容器、SpringMVC 的 DispatcherServlet、MyBatis 的 SqlSessionFactory光一个web.xml就要写一大段期间还容易把 jar 包版本搞冲突。SpringBoot 的价值是自动配置它把 SpringMVC 和 MyBatis 的常见配置通过 starter 默认处理好你只需要在application.yml里写数据库连接和少量自定义项。我的实际建议是毕设或者个人练手项目直接用 SpringBoot 2.7 配 mybatis-spring-boot-starter事务用Transactional参数校验用Validated没必要自己手拼 SSM 的 XML 配置除非你的考核要求里明确写了要展示 SSM 整合过程。SpringBoot 并没有替代掉 SSM而是把这些框架整合进自身的自动配置体系里使用体验更接近“开箱即用”。1.3 系统角色与业务闭环做商城系统前先想清楚有哪些人会用这套系统。我拆成三类角色普通用户、管理员、系统本身定时任务和状态流转。普通用户注册登录、浏览果蔬分类、搜索商品、加购物车、提交订单、模拟支付、确认收货。管理员登录后台、管理商品与分类、处理订单发货、管理用户、查看销售统计。系统自动流转订单创建、超时未支付取消可选、库存扣减、销量累加。这三类角色形成一个完整闭环用户下单 - 生成待付款订单 - 模拟支付 - 支付成功 - 管理员发货 - 用户确认收货 - 完成订单状态每一步都有据可查。我在设计时最重视“订单状态可追踪”这一点哪怕只是模拟支付也要把状态变化的时间节点记录下来这样后台看订单列表时不会一头雾水。2. 功能模块拆解与数据库设计的核心思路2.1 前台用户商城从搜索到下单的完整链路前台这一侧用户最先接触到的是首页。果蔬类商城首页我建议至少放这几块顶部导航分类入口、搜索框、轮播图新品或促销活动、果蔬分类展示区、热销商品榜。这里的分类不是简单做一级而是要有层级大类是水果、蔬菜子类是“柑橘类”“叶菜类”“根茎类”等用户点大类能看到子类再点子类才到商品列表这样更贴近超市的货架逻辑。商品列表页要支持按分类筛选、按价格或销量排序还要做分页。搜索功能很多人只做名称模糊匹配我建议把副标题、促销标签也纳入搜索范围比如用户搜“当季”能把带“当季水果”标签的商品搜出来体验会好一些。商品详情页则要展示主图、标题、价格、原价、库存、销量、商品描述注意生鲜商品最好标注产地和存储方式这些信息可以放在描述字段里也能提升真实感。购物车和结算流程是前台的核心。购物车要支持数量增减、删除、勾选、合计金额计算。结算时填写收货人、地址、电话生成订单订单金额要从购物车重新计算而不是前台传来多少就存多少这是最基本的后端校验逻辑。支付这块在课程设计里通常是模拟我的做法是生成订单后跳到一个“模拟收银台”用户点确认支付后端把订单状态从待付款改成待发货同时真实扣减库存、累加销量。2.2 后台管理端商品、订单、用户与统计后台管理端不要做成一堆功能堆叠要把“管什么”想清楚。商品管理包含分类管理、商品列表、商品上下架、库存调整、图片上传。果蔬类商品有个特点——库存变化频繁今天进货 50 斤卖到晚上剩 10 斤所以商品管理里一定要有直接修改库存的入口而不是只能靠下单自动扣。订单管理要有分页列表默认显示待发货和待付款点击详情能看到订单商品明细、收货信息、订单状态变更记录。管理员发货时填写物流单号可以是模拟订单状态从待发货变成待收货。用户管理相对简单列出注册用户支持禁用/启用账号必要的字段是注册时间、是否管理员、联系电话。统计模块不用做得太重我通常做每日订单数、每日销售额的折线图以及销量 Top10 商品列表数据来源就是订单表和订单明细表做聚合统计。2.3 数据库表怎么设计才不后面返工数据库设计是这类系统最容易返工的地方。我给的方案比较通用用户表、分类表、商品表、购物车表、订单表、订单明细表总共六张核心表再加一个可选的评论表。用户表字段id、用户名、密码BCrypt 加密存储、昵称、头像、手机号、角色、创建时间。分类表字段id、分类名、父级 id、排序、是否启用。用父级 id 实现二级分类比设计两张表简单得多。商品表字段id、分类 id、商品名、副标题、主图地址、单价、库存、销量、状态上架/下架、创建时间。这里我建议加一个sales字段虽然是冗余设计但查热销榜时性能好不用每次 COUNT 订单明细表。购物车表字段id、用户 id、商品 id、数量、勾选状态。订单表字段id、订单号唯一建议用时间戳加随机数生成、用户 id、总金额、状态、收货人、收货电话、收货地址、支付时间、发货时间、完成时间、创建时间。订单明细表字段id、订单 id、商品 id、商品快照名称、商品快照单价、数量。订单明细一定要做快照因为商品价格和名称随时可能改但已生成的订单不能跟着变。没有加外键约束全部用逻辑关联这是为了后续扩展和数据迁移方便。索引方面商品表的分类 id 和状态要建索引订单表的用户 id 和状态要建索引这些查询频率高加了索引后分页查询会快很多。3. 关键业务逻辑代码级的落地过程3.1 商品接口与果蔬分类的动态联动商品查询接口的核心就是一个带条件的分页查询用 MyBatis 的 XML 写动态 SQL 最方便。实体类对应商品表和分类表查询时通过categoryId和父级分类做联动处理选了父分类就查所有子分类下的商品。select idselectProductPage resultTypecom.example.entity.Product SELECT p.*, c.name as categoryName FROM product p LEFT JOIN category c ON p.category_id c.id WHERE p.status 1 if testcategoryId ! null AND p.category_id IN ( SELECT id FROM category WHERE pid #{categoryId} UNION SELECT #{categoryId} ) /if if testkeyword ! null and keyword ! AND (p.name LIKE CONCAT(%, #{keyword}, %) OR p.subtitle LIKE CONCAT(%, #{keyword}, %)) /if ORDER BY p.sales DESC /select这个 SQL 里用子查询实现“选了父分类自动包含子分类商品”比在 Java 层多次查询要省事。注意 MyBatis 的if判断里空字符串也要处理否则会带出一个无效的查询条件。写好之后 Controller 层只用接收分页参数和筛选条件调 Service 层分页查询返回统一的Result包装对象。3.2 购物车与订单状态机的实现购物车接口建议就三个加入购物车、修改数量、删除。加入购物车的时候先检查商品是否上架、库存是否充足再判断购物车表里有没有同款记录有就数量加一没有就新插入一条。返回给前端的数据里带上购物车商品数量和最新合计金额这样页面上显示“购物车(3)”这种提示不用额外请求接口。订单这块要设计好状态机。我用整数常量表示订单状态0 待付款、1 待发货、2 待收货、3 已完成、4 已取消。每次状态变更都要校验前置状态比如待发货只能由待付款变过来不能从已完成跳成待发货。生成订单是事务操作核心步骤先锁定购物车中选中的商品循环检查库存并扣减库存计算总金额然后创建主订单和明细最后清空购物车。Transactional public Order createOrder(Integer userId, ListCartItem cartItems, Address address) { Order order new Order(); order.setOrderNo(generateOrderNo()); order.setUserId(userId); order.setStatus(0); BigDecimal totalAmount BigDecimal.ZERO; for (CartItem item : cartItems) { Product product productMapper.selectByPrimaryKey(item.getProductId()); if (product null || product.getStatus() ! 1) { throw new ServiceException(商品不存在或已下架); } // 乐观扣减库存条件里带 stock num int rows productMapper.deductStock(product.getId(), item.getQuantity()); if (rows 0) { throw new ServiceException(product.getName() 库存不足); } totalAmount totalAmount.add( product.getPrice().multiply(BigDecimal.valueOf(item.getQuantity()))); } // 插入订单主表和明细表 order.setTotalAmount(totalAmount); orderMapper.insert(order); for (CartItem item : cartItems) { // 按最新查询到的商品信息写入快照 } cartMapper.deleteByUserIdAndProductIds(userId, cartItems); return order; }deductStock的 SQL 是关键UPDATE product SET stock stock - #{num}, sales sales #{num} WHERE id #{productId} AND stock #{num}这条 UPDATE 语句天然解决了高并发下的超卖问题如果返回值是 0 说明库存不足直接抛异常让整个订单回滚。项目里新建订单、扣库存、清购物车必须放在同一个事务里缺了Transactional就会出现库存扣了但订单没生成的问题我在联调阶段就踩过这个坑。3.3 登录态与统一异常处理登录认证我用的是拦截器加 Session。用户登录后把用户对象放进 Session然后注册一个拦截器拦截购物车、订单、个人中心这些需要登录的路径未登录的请求返回 JSON 提示前端跳登录页。不同用户之间的 Session 互不干扰Session 过期时间在application.yml里配置成 120 分钟防止用户逛着逛着突然掉线。统一异常处理用RestControllerAdvice做一个全局处理器。业务异常抛ServiceException参数校验异常抛MethodArgumentNotValidException兜底异常记录日志后返回统一错误信息。这样前端不管遇到什么异常拿到的 JSON 结构都一样处理逻辑简洁很多。我习惯在项目中用Result对象包装返回值结构是 code、message、data 三个字段code 为 200 表示成功其他值为失败。前端只认这个结构代码风格统一了联调效率高不少。3.4 销量统计的简单做法统计模块的思路是“订单明细表是唯一数据源”。每日销售额统计就是按支付时间分组 SUM 订单表中的总金额销量 Top10就是按商品 id 分组 SUM 订单明细表里的数量。这个查询商品信息要关联商品表用两个表 JOIN 加 GROUP BY 就能搞定。果蔬类商城有一个值得做的统计维度按分类汇总销售额。水果和蔬菜哪个品类卖得好对进货很有参考价值。查询时订单明细关联商品再关联分类对分类名称做分组聚合数据量小的时候这个查询在页面毫秒级返回。做图表时后端返回 JSON前端用 ECharts 渲染折线图和柱状图。这里我建议把日期参数做成必传没有日期范围就默认查最近 7 天否则统计接口容易被全表查询拖慢。4. 实际开发中踩过的坑与排查实录4.1 SpringBoot 与 MyBatis 整合的那些经典陷阱第一个坑是 Mapper 接口扫不到。SpringBoot 启动类没有加MapperScan(com.example.mapper)运行时就报Invalid bound statement (not found)。这个报错同时可能是 XML 文件没被加载需要在application.yml里配置mapper-locations。mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: truemap-underscore-to-camel-case一定要打开这样数据库字段create_time才能自动映射到实体类的createTime否则查出来全是 null。第二个坑是实体类字段类型数据库decimal对应 Java 类型要用BigDecimal金额计算不能用Double否则会有精度丢失问题。第三个坑是 XML 文件里符号在 SQL 中会被 XML 解析器当标签处理写小于号必须转义lt;比如stock lt; 100。4.2 PageHelper 分页功能为什么莫名其妙失效分页插件 PageHelper 是排障重灾区。我遇到过的情况是第二页数据返回第一页或者列表总数不对最后定位到两个原因。第一是在分页查询前执行了别的 SQL 查询PageHelper 的 ThreadLocal 会把前一个查询也分页掉。第二是分页查询后面的查询语句也被分页影响导致数据错乱。正确用法是 PageHelper 只紧跟第一条查询PageHelper.startPage(pageNum, pageSize); ListProduct list productMapper.selectPageByCondition(query); PageInfoProduct pageInfo new PageInfo(list);在分页查询这条语句执行完、拿到结果之后后面不允许立刻再查任何 SQL否则会串页。我后来直接把分页代码封装成一个工具类参数和结果类型固定团队其他人写的时候不容易用错。也可以用 MyBatis-Plus 的分页插件替代 PageHelper少一部分 ThreadLocal 的坑但如果在老项目上改造PageHelper 也能用明白。4.3 上传图片与静态资源映射问题果蔬类商品图片很重要没有图的生鲜商品根本没法看。上传模块我的做法是用 MultipartFile 接收文件保存到服务器本地目录文件名用 UUID 加时间戳重新生成避免中文名和重复名。但保存之后访问图片遇到过一个很恼火的问题——浏览器 404。原因是 SpringBoot 默认只映射classpath:/static/路径你上传到 D 盘或者/usr/local/upload的图片它根本不知道。解决办法是继承WebMvcConfigurer重写addResourceHandlers把上传目录映射成一个虚拟 URL 前缀Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceHandler(file: uploadDir); }这样图片地址就能用http://域名/upload/xxx.jpg访问。我踩过最隐蔽的坑是 Windows 和 Linux 路径分隔符不一致代码里不要写死/或\用File.separator拼接相对路径部署到 Linux 服务器上才不会出问题。4.4 库存超卖的并发问题关于超卖前面已经提到 UPDATE 语句带stock #{num}条件。但我最开始犯过的错是Product product productMapper.selectById(id); if (product.getStock() num) { throw new ServiceException(库存不足); } productMapper.deductStock(id, num);这种“先查再改”的做法在高并发下单场景下必出问题。两个请求同时读到库存还有 1都通过了判断然后都执行扣减库存就变成负数。数据库层面纠正这个问题的核心是让扣库存动作具有原子性单条 UPDATE 自带行级锁加条件校验后天然防超卖。下单场景中商品并发一般不会特别高这一点做对就够了。如果需要更强的保证还可以扣减时根据返回值判断返回值 0 就回滚订单事务用户体验上表现为“手慢了库存不足”。5. 项目体验优化与部署心得5.1 前后端联调时要留意的几个体验细节先说一下页面展示层的问题。果蔬是生鲜商品用户最在意新鲜度我在商品列表和详情页都显示了“上架时间”和“产地”信息同时把库存紧张的商品显示“仅剩 3 件”的标签这个细节很能提升商城的真实感。搜索热词功能也可以做一下前端把用户搜索记录存到 localStorage在搜索框下拉展示这个功能不依赖后端但用户体验提升明显。购物车空状态、订单空状态的占位图也处理一下不要让人点进去白屏一片。联调阶段我遇到过商品图片加载不出来导致布局错乱的问题原因是某条商品记录图片字段为空前端没有兜底后来统一在图片 src 上加了默认图逻辑。管理后台的数据统计图表出现中文乱码大概率是请求返回的 JSON 没有设置编码在 SpringBoot 里加一个CharacterEncodingFilter强制 UTF-8 就能解决。5.2 部署到云服务器前的配置整理开发环境跑通之后部署最容易出问题的不是代码是环境配置。我把部署流程整理成固定步骤先准备一台 Linux 服务器装好 JDK 1.8或 JRE 17看 SpringBoot 版本、MySQL 8。数据库导入时要注意字符集和时区连接字符串加上useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai否则时间差 8 小时会让人排查到怀疑人生。项目里用 Maven 打包前先把application-dev.yml改名为只保留application.yml数据库连接改成线上地址。打包命令我固定用 Maven 跳过测试打包mvn clean package -DskipTests在服务器上启动用nohup java -jar mall-system.jar 启动时加服务器内存限制-Xms512m -Xmx512m免得云服务器内存不够触发 OOM。日志输出要有一个固定位置推荐用--logging.file.path/data/logs排查问题时tail -f看日志很方便。图片上传目录要提前建好并保证运行用户有写权限部署完先走一遍完整下单流程确认图片可访问、支付回调正常、后台发货正常再算上线成功。我自己做完这套系统的感受是功能本身不难难的是把业务主线想清楚、把边界情况考虑到位。你照着这个思路做超市果蔬的商城系统基本能每天跑得稳稳的。
阅读完成 · 觉得有帮助?
咨询建站