简介:这是一套基于SSM框架与MySQL的生鲜配送系统完整毕业设计项目,适合Java后端初学者、高校毕设学生以及需要快速搭建电商类管理后台的开发者。项目按管理员与用户双角色设计,后台覆盖商品分类、上下架、库存增减、积分记录、订单管理等典型业务;前台包含用户注册登录、商品展示、购物车、立即购买、余额与积分充值、地址管理、收藏评论等功能,业务链路完整,便于二次扩展。压缩包共1319个文件,约18.16MB,以JSP页面、Java源码、CSS/JS脚本为主,另有数据库SQL脚本、配置文件及文档,整体结构清晰,部署时按说明导入即可。目前已有202人学习下载,可作为毕设答辩、SSM整合练习或生鲜电商原型参考。资源附带源码与数据库脚本,能帮助读者理解SSM分层开发、Maven依赖管理及前后台交互流程,是一份高性价比的实战学习资料。 每年毕设选题阶段,Java方向里“SSM框架+MySQL”的组合永远是重头戏,“生鲜配送系统”也基本是电商类选题里出现频率最高的几个之一。不少人一听“SSM”觉得老气,但实际操作下来你会发现,SSM的好处恰恰是“屏幕后的坑全部公开”。Spring管对象,Spring MVC管路由,MyBatis管SQL,分层清楚;MySQL这边表多但又不算太多,业务复杂度刚好比普通的图书管理系统高一个台阶,比你想象中更适合撑起一篇完整的毕业论文。这篇文章我按平时带人做代码评审的思路,从选题逻辑、ER图设计、SSM整合、下单事务,到源码整理和论文素材配合,给你完整拆一遍。
1. 为什么“SSM+MySQL生鲜配送”一直是毕设选题的稳牌
1.1 业务复杂度刚好撑起一篇论文
选题最怕两种极端:一种是“图书管理”“宿舍管理”这类系统,数据库就三四张表,演示三分钟结束,答辩时老师问两句就冷场;另一种是直接上分布式、微服务、高并发,代码堆了两万行,但一问到底,很多模块只是空壳,反而扣分。
生鲜配送系统恰好卡在中间。它的核心业务链路包含用户注册登录、商品分类浏览、购物车、下单、库存扣除、订单状态流转、配送信息管理、后台商品上下架,这些需求拆完之后,数据库表少说也有七八张,表之间的关联、事务、状态字段设计都有得写。生鲜行业还有自己的特点:商品有保鲜期、存储方式、单位规格、起购数量,订单里需要保存商品快照和地址快照,防止商品信息后续变化影响历史订单。这些细节会让你的系统显得“做过功课”,而不是简单套一个电商模板。
1.2 SSM这套组合反而更适合答辩拆解
不是所有项目都非要上Spring Boot。Spring Boot 确实简化了开发,但它把大量配置黑盒化了,论文里写“框架原理”的时候,你会发现很难讲清楚请求到底怎么从浏览器走到Controller、MyBatis又是怎么被Spring管理的。
SSM的好处是每一步都在配置里显式暴露出来。applicationContext.xml 管理数据源和Service,spring-mvc.xml 管理Controller扫描和视图解析,mybatis-config.xml 管理别名和映射。答辩老师一追路径,你能顺藤摸瓜讲清楚整个调用链。而且SSM项目可以部署在Tomcat上,war包扔进去就能跑,对很多学校机房环境更友好。
当然,SSM也有它的代价:依赖版本之间的兼容性问题比Spring Boot多不少,第一次搭环境很容易被各种异常劝退。这个我在后面单独开一节讲,因为这正是源码放你手里但跑不起来的最大原因。
2. 动工前的业务建模:先梳理角色和订单状态流转
2.1 角色划分不用贪多,三个角色加权限就够
很多毕设项目喜欢把角色拆成“超级管理员、普通管理员、配送员、用户”一堆,最后每个角色只有一两个接口,显得臃肿。建议只做两类核心角色加一个配送状态:
- 前台用户:注册、登录、浏览商品、管理购物车、下单、支付模拟、查看订单、确认收货、维护收货地址。
- 后台管理员:分类管理、商品管理、用户管理、订单状态的发货处理。
- 配送环节:不需要单独建角色表,用订单里的配送字段和状态值控制即可,比如管理员发货时填入配送员姓名和电话,用户端看到“配送中”。
权限控制在SSM里最朴素的实现方式就是拦截器。登录以后把用户对象放进Session,拦截器里判断Session是否存在,再根据用户类型决定是否能访问后台路径。这比引入Spring Security要轻得多,而且答辩时你能清楚解释拦截器原理。
2.2 订单状态是整张数据模型的地基
订单状态不要用字符串到处散落,直接统一用数字定义,并在代码里写常量类或者枚举。我见过不少项目service里随手写“1”“2”“3”,后来改需求全身找,非常痛苦。这里给一个可以直接照抄的状态设计:
| 状态值 | 含义 | 触发动作 |
|---|---|---|
| 0 | 待支付 | 下单成功,未模拟支付 |
| 1 | 待发货 | 支付成功/模拟支付完成 |
| 2 | 配送中 | 管理员后台点击发货 |
| 3 | 已完成 | 用户确认收货 |
| 4 | 已取消 | 未发货前用户取消或超时取消 |
这套状态流转还有一个重要规则:已发货的订单不能取消,所以取消操作必须在Service层先判断status。很多同学直接对着DAO写update。等演示时发现“配送中的订单被我点了一下就取消”就很尴尬。业务校验放在service层,不要写死在页面里。
3. 数据库设计:MySQL里的表和字段到底怎么落
3.1 核心表结构一版直接可用
这里给出一套我整理后比较稳的表结构,建表时基本可以照着写:
- user:id、username、password、phone、create_time。密码不要明文存,建议MD5加盐或至少简单的密码处理,论文里也能提一句安全考虑。
- category:id、name、sort。
- product:id、category_id、name、main_image、price、stock、sales、status、shelf_time、storage_type。storage_type这个字段是生鲜特色,存“冷藏/冷冻/常温”之类的信息,前台商品详情能展示,论文里也能作为系统区别于普通电商的小亮点。
- cart:id、user_id、product_id、quantity。
- address:id、user_id、receiver、phone、province、city、district、detail、is_default。
- orders:id、order_no、user_id、total_amount、status、receiver_snapshot、phone_snapshot、address_snapshot、remark、create_time、pay_time、ship_time、finish_time。
- order_item:id、order_id、product_id、product_name、product_image、price、quantity。
两条建表原则非常重要。第一,订单表里要存“地址快照”和“商品名称/价格快照”,不要在下单时只存一个product_id就完事。因为商品改价、地址修改后,历史订单应当不受影响。第二,所有涉及金额的字段用decimal(10,2),不要用float或double,否则运算时会出现精度问题,答辩时一问一个准。
字符集方面,MySQL 8.0默认就是utf8mb4,一般不需要额外折腾,但如果你是导入别人给的sql文件,务必确认文件里的建表语句是不是utf8mb4,不然中文会出现乱码。引擎用InnoDB,外键建议逻辑关联就行,不用真在表上建物理外键,开发阶段写SQL灵活,性能也不容易受约束。
3.2 库存扣减的超卖问题
生鲜配送里有促销场景,就一定会涉及到库存扣减。最典型的错误写法是:
Product product = productMapper.selectById(id); int stock = product.getStock(); product.setStock(stock - quantity); productMapper.updateById(product);这段逻辑在单用户时没问题,但一旦有点并发压力,两个请求同时读到同一个stock,就会出现超卖。更稳的做法是把判断和扣减放在一条update里:
update product set stock = stock - #{quantity} where id = #{id} and stock >= #{quantity}然后用受影响行数判断是否扣减成功,如果返回0说明库存不足,直接抛异常回滚。这个方案代码改动小、逻辑清晰,而且回答“怎么防止超卖”时非常好讲。如果你想在论文里再拔高一点,可以提“乐观锁”思路:加一个version字段,更新时比较version。两者原理类似,但上面这条SQL已经够用,不推荐为了炫技把系统搞复杂。
4. SSM整合的关键步骤和跑不起来的原因排查
4.1 依赖版本搭配:最容易被坑的地方
如果你拿到一个SSM源码,导入后各种报错,八成问题出在三个地方:jar包版本冲突、数据库驱动类名不对、字符集连接串没写对。
先看pom.xml里的版本搭配。Java 8搭配Spring 5.x、Spring MVC 5.x、MyBatis 3.5.x、mybatis-spring 2.0.x是我现在比较推荐的组合。注意,mybatis-spring 1.x和Spring 5放在一起经常出现诡异异常,一旦升级就顺手把mybatis-spring升到2.x。MySQL驱动方面,MySQL 8.x数据库不要用mysql-connector-java 5.1.49以下的老驱动,驱动类名是com.mysql.cj.jdbc.Driver,不是com.mysql.jdbc.Driver,后者在MySQL 8下直接报ClassNotFoundException。
连接串也建议写成下面这样,避免时区问题:
jdbc:mysql://localhost:3306/fresh_delivery?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false4.2 三个配置文件的职责要分清,别全塞一起
SSM项目里至少有三个配置文件,各管一段:
- applicationContext.xml:数据源、SqlSessionFactory、Service扫描、事务管理器。
- spring-mvc.xml:Controller扫描、注解驱动、视图解析器、静态资源放行。
- mybatis-config.xml:MyBatis全局设置,比如下划线转驼峰、日志实现。
一个非常能体现你水平的小设置是在applicationContext.xml里给SqlSessionFactory配上typeAliasesPackage和mapperLocations,这样Mapper XML不用在代码里写资源路径,Service里实体类也能用短名称。
事务管理建议用注解方式:在spring-mvc.xml或applicationContext.xml里配置<tx:annotation-driven transaction-manager="transactionManager" />,然后在service实现类上加@Transactional(rollbackFor = Exception.class)。注意rollbackFor一定要写,否则默认只在RuntimeException时回滚,数据库操作时出现的受检异常不会触发回滚,库存扣一半就麻烦了。
4.3 MyBatis映射的日常细节
打开MyBatis的驼峰映射:在mybatis-config.xml里写<setting name="mapUnderscoreToCamelCase" value="true"/>,这样数据库的create_time能自动映射到createTime,不用手工写一堆resultMap。
写动态SQL是SSM的日常,最常见的是列表条件查询,这里直接给一段可以复用的写法:
<select id="selectOrderPage" resultType="com.example.entity.Order"> select * from orders <where> <if test="status != null"> and status = #{status} </if> <if test="orderNo != null and orderNo != ''"> and order_no like concat('%', #{orderNo}, '%') </if> <if test="startDate != null"> and create_time >= #{startDate} </if> <if test="endDate != null"> and create_time <= #{endDate} </if> </where> order by id desc limit #{pageNo}, #{pageSize} </select><where>标签会自动处理掉第一个条件前面的and,这个特性很实用。分页别乱用“select count”,可以先按普通limit写,系统跑通之后再考虑引入PageHelper,这样生成的论文里“分页模块”才能写出前后两版优化对比,反而更有内容。
5. 核心模块实现:登录、下单、订单列表一条链
5.1 登录会话管理用拦截器,不搞花活
登录模块如果自己写Session不太好扩展,建议抽成HandlerInterceptor。核心逻辑只有三行判断:
public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); User user = (User) session.getAttribute("user"); if (user == null) { response.sendRedirect(request.getContextPath() + "/user/toLogin"); return false; } return true; } }然后在spring-mvc.xml里配置拦截路径,注意把登录接口、注册接口、静态资源都放行。建议分成两个拦截器:一个拦截前台接口,一个拦截后台接口,后台接口还要判断user.getType()是否为管理员。这种写法足够讲清楚“认证+授权”在项目里是怎么落地的。
5.2 下单流程一定要包在同一个事务里
订单模块是整个系统的价值核心,伪代码如下:
@Transactional(rollbackFor = Exception.class) public void createOrder(CreateOrderRequest request) { Order order = buildOrder(request); orderMapper.insert(order); for (CartItem item : request.getItems()) { int rows = productMapper.deductStock(item.getProductId(), item.getQuantity()); if (rows == 0) { throw new BusinessException("库存不足:" + item.getProductName()); } OrderItem orderItem = buildOrderItem(order.getId(), item); orderItemMapper.insert(orderItem); } cartMapper.clearCart(request.getUserId()); orderMapper.updateStatus(order.getId(), 0); }这里有几个细节值得展开。第一,deductStock就是前面那条update product set stock = stock - #{quantity} where id = #{id} and stock >= #{quantity}。第二,循环里插入order_item前,根据productId查出最新的商品价格和名称写进快照,不要从前端传价格,否则用户可以改请求参数。第三,下单结束后把购物车清空,这个操作也必须和订单插入在同一个事务里,否则订单有了,购物车还留着,演示时怎么看怎么别扭。
5.3 订单列表的状态筛选和搜索
订单列表页通常要支持按状态切换、按订单号模糊查询、按时间段筛选。推荐在Controller层接收这几个参数,组装成Map传给Mapper,动态SQL来拼接条件。列表排序就按id倒序,不要按create_time倒序,因为同一时间创建的订单多时,id排序更稳定。
订单详情页建议使用OrderDetailVO,把订单表、订单明细表、地址快照一起封装成视图对象,不要在前端分别调三个查询接口再拼数据。这里也体现分层意识:Controller不要直接操作DAO,Service返回组装完成的VO。
6. 源码组织与毕业论文如何互相“长脸”
6.1 源码结构决定老师第一印象
拿到源码的同学最怕的是打开工程包发现代码全放在一个类里,到处都是System.out.println。建议分包干净一点:
- entity:数据库实体类
- dao / mapper:MyBatis接口
- service + impl:业务接口和实现
- controller:请求入口
- interceptor:登录、管理后台拦截器
- common:统一返回结果、常量类、异常处理类
- util:工具类
一个很讨喜的细节是在项目根目录放一个README.md,写上JDK版本、Tomcat版本、MySQL版本导入方式、修改哪些连接配置、初始账号密码。别小看这个文件,评委验收项目时第一件事就是看能不能照着重现,README写得好,直接省掉双方大量无效沟通时间。
另一个细节:数据库脚本里除了建表语句,顺手插入几条分类、商品、管理员的初始数据。空表页面会很丑,有数据才能演示加购、下单、发货、确认收货的完整链路。
6.2 论文的功能与数据库设计必须处处对得上
论文结构可以按需求分析、总体设计、详细设计、系统实现、系统测试来写,但有两处特别容易露馅,需要你在开发阶段就同步维护。
第一是ER图。评委会拿ER图和实现代码里的表做对比,出现多了表或者缺了表都是硬伤。我的做法是:先把数据库表全部落地,再照着表反推ER图,保证一一对应,然后再开始画用例图。
第二是测试章节,很多同学只写“系统运行正常”之类的话,没有任何可复现数据。建议至少在测试部分写两个有价值的用例:一个是对库存不足的下单操作,预期是“库存未扣减并且提示库存不足”;另一个是未登录用户直接访问后台接口,预期是“被拦截器重定向到登录页”。这两个用例能展示你对事务回滚和权限控制的思考,而且是真实能跑通的。
最后再分享一个实操经验
这套系统如果从零开始动手,我建议不要按章节顺序平推,而是按“用户登录 → 商品列表 → 购物车 → 下单 → 订单列表 → 后台发货”这条纵线先把主流程拉通。第一次跑通的感觉是很关键的,后面再补充地址管理、商品上下架、状态筛选这些横切功能就快很多。我自己帮人排查过很多“源码跑不起来”的案例,最终原因无非就是MySQL版本对不上、Tomcat编译级别不对、连接串没改这三类。动手第一步先准备好干净的环境,JDK 1.8、Tomcat 8.5、MySQL 8.0这套组合认证率最高。刚开始慢一点没关系,把开源组件之间的版本关系理清楚,比赶进度多撸几页代码要重要得多。
本文还有配套的精品资源,点击获取