在准备计算机毕业设计选题的时候很多同学都会在“系统要够完整”和“自己能写得出来”之间反复纠结。宠物商城这个方向是我比较推荐的一个选择电商业务的闭环非常清晰从商品展示、购物车、订单到后台管理每一环都能对应到Java Web开发的核心知识点而且“宠物商城”的主题在评审时也容易讲出亮点。这里就把我做这套基于Java Web的宠物商城系统时的完整思路、技术选型、数据库设计、核心代码实现以及踩过的坑一次说清楚希望能给正在做同类项目的朋友一些参考。1. 项目需求拆解与整体设计1.1 宠物商城到底要做哪些事毕业设计最容易翻车的地方就是一开始就把需求想得太大。我见过有人把秒杀、优惠券、直播带货全部塞进一个商城系统结果写了两个月还在改BUG。宠物商城这个题目本质上是一个标准的B2C电商系统核心业务就四块商品展示、购物车、订单流转、后台管理。展开来说前台用户能做的操作无非是注册登录、浏览商品、按分类筛选、搜索、查看详情、加入购物车、结算下单、查看订单状态后台管理员能做的就是商品的上架下架、库存管理、分类维护、订单发货处理、会员信息查看。这些功能看起来多但每个功能都是Java Web课程里反复出现的基础操作组合起来就是一个完整的、有说服力的毕业设计题目。我在做需求分析的时候用了最简单的角色-用例图方式梳理。系统只有两种角色普通用户和管理员。用户端的功能走“浏览-加购-下单-支付-查看订单”这条线管理端的功能走“商品维护-订单处理-数据查看”这条线。有些同学会纠结要不要加“支付”功能我的建议是不要真的接第三方支付接口做一个“模拟支付”状态即可即用户点击“确认支付”后订单状态从“待支付”变为“已支付”后台再走发货流程。这样既展示了业务闭环又不会因为接入支付SDK而给自己挖坑。1.2 角色划分与功能边界把功能边界画清楚后面写代码的时候会轻松很多。我最终落地的功能清单如下用户端注册、登录、退出、商品列表含分类筛选和关键词搜索、商品详情、加入购物车、购物车管理增删改数量、提交订单、模拟支付、查看订单列表、订单详情、个人资料修改。管理端管理员登录、商品管理增删改查、上下架、分类管理、订单管理查看订单详情、修改状态为已发货、用户管理查看用户列表、禁用/启用账号。这里要注意一个容易被忽视的点管理员登录和用户登录必须分开处理。最简单的方式是users表里加一个role字段1代表管理员0代表普通用户。登录成功后在Session中保存用户的角色信息后台接口统一做一次角色校验非管理员直接拦截跳转。这样做虽然简单但对毕业设计来说已经够用而且评审老师问起来你能清楚说出“基于角色控制的权限管理”这个设计思路加分效果很明显。1.3 为什么选择B/S架构Java Web的毕业设计绝大多数都会采用B/S架构也就是浏览器/服务器模式。原因很简单用户不需要安装任何客户端打开浏览器输入地址就能访问部署和演示都很方便。我在答辩演示的时候就是直接在演示机上启动项目然后用另一台设备打开浏览器访问整个过程没有装任何额外软件。B/S架构下浏览器负责页面展示和交互服务器Tomcat负责处理业务逻辑和数据读写。数据存储在MySQL中服务器通过JDBC或者ORM框架访问数据库。这种分层方式天然契合MVC思想JSP/HTML是View层Controller层接收请求并调用ServiceService层处理业务逻辑DAO层完成数据库操作。我在写代码时严格遵循这个分层每个类的职责都很单一后期调试的时候基本上看类名就能定位问题。2. 技术选型与关键原理2.1 Java Web技术栈怎么选技术选型直接决定你后面几个月写代码的心情。最常见的组合是Spring Boot MyBatis MySQL Maven JSP/HTML Bootstrap Layui。这套组合的优点是文档多、资料全、踩坑记录遍地都是遇到问题搜索一下基本都有答案。先说Spring Boot。现在做Java Web项目再手动配置一大堆XML就太折磨自己了Spring Boot的自动配置特性把大量的配置工作都省掉了。创建一个Spring Boot项目之后只需要在application.yml里写数据源配置和MyBatis的mapper地址项目就能跑起来。这比传统的SSHStrutsSpringHibernate或者SSM手动配置要高效太多尤其对时间紧张的毕业设计来说省下的时间可以用来打磨界面和写论文。数据库我选了MySQL 8.0。原因无他使用人数最多、图形化工具成熟Navicat、Workbench都行、网上教程丰富。要注意MySQL 8.0的驱动类名和连接串和5.x不一样这点我在后面“踩坑记录”里详细说。2.2 Spring Boot的核心价值很多同学在写项目时只知道用Spring Boot却说不清楚它到底解决了什么问题。这个在答辩时非常容易露馅。我用自己的话解释一下Spring Boot的价值传统的Java Web项目你需要手动配置web.xml、Spring配置文件、MyBatis配置文件、事务管理器、视图解析器等等每加一个功能就要去翻配置文档。Spring Boot把这些繁琐的配置变成了“约定优于配置”。你引入spring-boot-starter-web依赖它就自动帮你配置好内置的Tomcat和Spring MVC你引入mybatis-spring-boot-starter它就帮你自动创建SqlSessionFactory。开发者只需要关注自己的业务代码。打个比方以前做饭需要自己生火、砍柴、搭灶台Spring Boot相当于给你一个现代厨房你只需要炒菜就行。这个类比在答辩时解释给评委听效果比背定义好很多。2.3 MyBatis还是JPA我一直推荐用MyBatis而不是JPA原因就是MyBatis的SQL是显式写在Mapper.xml里的你完全知道每一步在查什么。而JPA通过方法名推导SQL很多新手搞不清楚底层到底执行了什么出了问题难以排查。举个例子查宠物商品列表时如果要支持按分类和关键词动态过滤用MyBatis只需要在Mapper.xml里写一个动态SQLselect idselectByCondition resultTypecom.demo.pet.entity.Pet SELECT * FROM pet where if testcategoryId ! null AND category_id #{categoryId} /if if testkeyword ! null and keyword ! AND name LIKE CONCAT(%, #{keyword}, %) /if /where ORDER BY create_time DESC /select这种做法清晰明了实验结果也好复现。而用JPA时这种动态条件往往要写Specification或者QueryDSL理解成本高了不少。MyBatis的另一个优势是SQL优化方便比如给表加索引、改写join语句直接在XML里调整就行。2.4 前端方案取舍很多做Java后端的人一听到前端就头疼我完全理解。我的建议是不要自己手写复杂的前端交互找一个现成的后台管理模板然后改改样式、替换数据就行。比如Bootstrap搭一个简洁的前台页面后台管理直接用Layui的admin模板表格展示、分页条、表单弹窗这些都帮你封装好了你只需要按照约定的JSON格式把数据返回给前端。我当时的做法是前台商城页面自己用Bootstrap JQuery编写页面包括首页、商品列表页、商品详情页、购物车页、订单页、个人中心页。用Thymeleaf模板引擎渲染数据不搞前后端分离这样项目结构更简单。后台管理页面采用Layui的admin框架左侧菜单栏、右侧内容区表格用Layui的table模块弹窗用layer模块。所有的后台操作都通过Ajax请求完成。这样选型的核心逻辑是毕业设计的重点在Java后端逻辑不在前端炫技。你只要把Bootstrap栅格布局会用元素样式能调表格能展示数据就已经超过很大一部分人了。3. 数据库设计表的拆分与关联3.1 核心表清单数据库设计是整个项目的地基表建得合理写代码的时候行云流水表建得不合理后期改起来痛不欲生。我这个项目一共设计了6张核心表下面把每张表的用途和关键字段理一下表名用途关键字段user用户表存储用户和管理员id, username, password, phone, role, status, create_timecategory商品分类表id, name, sort_orderpet宠物商品表id, category_id, name, price, image, stock, sales, description, status, create_timecart购物车表id, user_id, pet_id, quantity, create_timeorders订单表id, order_no, user_id, total_price, status, create_time, pay_time, ship_timeorder_item订单明细表id, order_id, pet_id, pet_name, price, quantity这里有一个非常关键的设计决策购物车中的商品信息可以只存pet_id和quantity因为购物车是临时性的会话结束后数据可以丢但是订单必须把下单时的商品快照存下来比如pet_name、price因为商品之后可能改名、改价甚至下架而订单作为历史记录必须保留下单那一刻的真实信息。这个“快照”设计在答辩时是一个很好的加分点。3.2 订单与商品的关系处理订单和商品之间是多对多的关系一个订单包含多个商品一个商品可以被多个订单购买所以必须加一张中间表order_item。order_item里不仅存pet_id还要冗余pet_name和price原因刚才说了——历史快照。库存扣减逻辑也很重要。用户下单时系统要检查该商品的库存是否足够如果足够则扣减库存如果不够则提示库存不足。我在Service层做了事务控制保证“创建订单主表记录、创建订单明细、扣减库存”这三步要么全部成功要么全部回滚。如果不加事务可能会出现订单创建成功但库存没扣减的脏数据评审老师一眼就能看出问题。一个值得分享的设计细节是订单号的生成。我在Java中用时间戳加随机数的组合String orderNo System.currentTimeMillis() String.format(%04d, new Random().nextInt(10000));这样生成的订单号虽然不保证绝对唯一但在单机项目中已经足够使用。如果想要更严谨可以在数据库中给order_no加唯一索引防止极端情况下的重复。3.3 图片存储方案商品肯定要展示图片这里我强烈建议图片不要存数据库不要存数据库不要存数据库。正确做法是把图片文件放在项目的静态资源目录下数据库只存储图片的相对路径比如/images/pets/dog1.jpg。我在项目里是这样处理的在src/main/resources/static/images/pets/下建立商品图片目录图片文件名用商品ID加随机后缀命名避免文件名冲突。前端img标签的src直接绑定数据库里存的路径即可。这样做的好处是页面加载快、数据库表体积小、备份方便。很多同学会在答辩前为图片上传功能发愁完全不用。最简单的做法是在后台商品管理页面提供一个图片选择功能上传后把文件保存到指定目录然后返回路径。用Spring Boot的MultipartFile接口可以很轻松地实现文件上传处理代码量不超过20行。4. 核心功能实现要点4.1 注册登录与Session会话管理登录注册模块是每个系统的门面也是几乎所有Java Web课程的第一课但恰恰是这里容易出问题。我的实现方案是注册时对用户提交的密码做MD5加密后存入数据库登录时把用户输入的密码加密后和数据库比对比对成功则把用户对象存入Session。为什么加密如果密码明文存库一旦数据库泄露所有用户的密码直接暴露。虽然毕业设计不涉及真实用户数据但养成良好的安全习惯是必须的。MD5现在来看强度不够可以在加盐后使用比如MD5(password salt)。我在代码里加了一个固定盐答辩时完全可以讲出“密码加盐哈希”的安全设计思路。登录校验的代码模式基本如下User user userService.findByUsername(username); if (user null || !user.getPassword().equals(MD5Utils.md5(password SALT))) { return 用户名或密码错误; } if (user.getStatus() 0) { return 该账号已被禁用; } session.setAttribute(loginUser, user);还有一个细节是“未登录拦截”。我写了一个拦截器对需要登录才能访问的路径比如购物车、订单提交、个人中心做校验未登录则重定向到登录页。拦截器的配置在Spring Boot里只需要实现HandlerInterceptor接口再注册到WebMvcConfigurer中。4.2 商品检索与分页商品列表页是最常被评审老师检查的页面因为这里是用户看到的首页入口。商品展示的两个核心功能是条件查询和分页。条件查询的SQL用MyBatis动态SQL实现上一节已经展示过。分页这里我要特别提醒千万不要自己用LIMIT手动算分页虽然那样也能做但代码很啰嗦。直接用MyBatis的分页插件PageHelper两行代码搞定PageHelper.startPage(pageNum, pageSize); ListPet petList petMapper.selectByCondition(categoryId, keyword); PageInfoPet pageInfo new PageInfo(petList);PageHelper的原理是在执行查询前自动拦截SQL拼接上LIMIT子句然后查询总条数封装成PageInfo。用这个插件前端只需要传pageNum和pageSize后端返回的数据中包含总条数、总页数、当前页数据列表前端就能渲染出完整的翻页组件。这个处理方式我在答辩时讲了很久因为评委对分页性能和数据正确性非常关注。4.3 购物车设计购物车有不同的实现方式最简单的是存数据库先进一点的是存Redis。毕业设计场景下存数据库就足够了。购物车的表结构就是user_id加pet_id加quantity再加一个唯一约束(user_id, pet_id)保证同一个用户对同一件商品只有一条记录。当用户再次添加同一商品时不新增记录而是把quantity加1。这个逻辑在Service层判断Cart cart cartMapper.findByUserIdAndPetId(userId, petId); if (cart null) { // 新增记录 } else { // 更新数量 cart.setQuantity(cart.getQuantity() quantity); cartMapper.updateById(cart); }购物车页面展示的时候需要联查出pet的当前价格和图片。我的做法是写一个JOIN查询从cart表关联pet表把需要展示的字段全部查出来封装成VO对象。这里让我明白一个道理VO是视图层需要的数据形态不一定和数据库表结构一一对应。在写VO时你可以把关联信息、格式化后的价格等一次性准备好前端直接拿数据展示。4.4 订单流程订单流程是整条业务链中最重要的一环也是我花费时间最多的地方。用户从购物车点击“去结算”后进入确认订单页页面展示所有商品、数量、总价确认后提交订单。后端处理订单的逻辑分这几步查询购物车中该用户的所有商品检查每个商品的库存是否充足创建订单主表记录状态为“待支付”创建订单明细记录每个商品一条扣减商品库存清空购物车返回订单号给前端这7步必须在同一个事务中执行。我在方法上加Transactional注解保证任何一步失败都会整体回滚。在之前的测试中我特意把库存扣减故意写错然后观察数据库发现事务回滚后订单记录也没有了这才算是真正理解了事务的意义。订单状态我设计为待支付 - 已支付 - 已发货 - 已完成。用户操作触发状态从“待支付”到“已支付”管理员操作触发从“已支付”到“已发货”。这里我用一个简单的状态码字典维护代码中用常量表示避免魔法数字散落各处。4.5 后台管理模块后台管理模块的核心是商品管理、订单管理和用户管理。商品管理最繁琐的地方是“编辑”功能商品信息包括分类、价格、库存、描述、图片表单字段多校验也麻烦。我用Layui的弹窗表单实现新建和编辑共用一个表单通过请求参数中的id区分是新增还是更新。订单管理最简单也最容易忽略细节。订单列表展示时需要把订单中的商品明细一并显示这时就用到了前面设计的order_item表。点击“查看详情”时页面展示订单基本信息、收货人信息、商品列表、总价和状态变更时间线。这里可以用一个联表查询把orders、user、order_item三张表的数据查出来组装成VO返回。后台的权限控制我用了最简单的方法在Controller类上加一个拦截器对/admin/**路径统一校验Session中的role是否为管理员。这个方案虽然不够精细但足够让评审老师看到你考虑了权限问题。5. 项目部署与源码使用5.1 本地环境搭建如果你拿到的是别人写的源码第一件事不是启动而是先看懂项目的结构。一个标准的Spring Boot项目结构应该是src/main/javaJava源码src/main/resources配置文件、静态资源、MyBatis的Mapper XMLpom.xmlMaven依赖配置database.sql数据库初始化脚本如果作者放了的话本地启动的步骤如下安装JDK 8或11配置JAVA_HOME环境变量。安装Maven 3.6以上版本配置镜像源为阿里云加快依赖下载速度。安装MySQL 8.0启动服务。用Navicat或命令行执行database.sql脚本创建数据库和表结构。修改application.yml中的数据库用户名和密码。在项目根目录执行mvn spring-boot:run启动项目。浏览器访问http://localhost:8080。5.2 数据库初始化注意事项很多源码附带的SQL脚本分为两种一种是只建表一种是建表并插入初始数据。一定要确认脚本执行后是否有初始数据。如果没有初始数据项目启动后商城页面是空的你可能以为系统出了BUG其实只是数据库没有数据而已。我习惯在SQL脚本中默认插入几个分类比如狗、猫、小宠、水族每个分类下插入5条左右的宠物商品示例数据并设置一个默认管理员账号。这样一次性把基础数据准备好后续开发和演示都非常方便。管理员账号的密码记得也要用加密后的字符串存入不能存明文。5.3 启动与验证项目启动后按下面路径验证系统是否正常访问首页确认商品能正常显示。注册一个新用户登录。将一件商品加入购物车。提交订单并模拟支付。在后台用管理员账号登录查看订单并修改状态为“已发货”。回到前台用户端刷新订单列表看到订单状态已经更新。这一整条链路走通项目功能就算完整了。6. 踩坑记录与调试心得6.1 分页查询的SQL报错第一次用PageHelper的时候发现查询结果一直不对后来查日志发现是PageHelper拦截了Mapper查询但我的SQL语句最后一个字符是分号导致拼接LIMIT时出错。排查了很久。所以写SQL时不要在多行语句末尾加分号MyBatis的Mapper XML中更不要加分号否则很容易出现各种奇奇怪怪的SQL拼接问题。6.2 图片加载不出来的问题开发环境很好到了部署环境图片全部404。原因是Spring Boot默认静态资源路径是classpath:/static/如果把图片放在项目外部的磁盘路径中需要额外的配置映射。我的解决方案是写一个配置类把/images/**映射到本地磁盘目录Configuration public class WebConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/images/**) .addResourceLocations(file:D:/pet-mall-images/); } }这样上传的图片都存到D盘固定目录不依赖项目包路径打jar包部署后图片也不会丢。6.3 事务不生效问题给方法加了Transactional结果发现数据还是没回滚。排查后发现是因为我在同一个类中调用被事务标记的方法Spring AOP默认是基于代理的同类内部调用不会经过代理事务自然不生效。解决办法有两个一是把被调用的方法拆分到另一个Service类中二是在调用时从容器中获取代理对象。毕业设计场景下第一种方式更简单直观。这里也建议尽量遵守“Controller只做转发不做业务Service专心做业务事务加在Service层”的分工。6.4 MySQL 8.0驱动的坑MySQL 8.0的JDBC驱动类名是com.mysql.cj.jdbc.Driver连接串需要带serverTimezoneAsia/Shanghai参数否则DateTime相关字段会报错。这个错误非常经典几乎所有从5.x升到8.0的人都遇到过。如果你的MySQL是5.x驱动还是com.mysql.jdbc.Driver连接串不需要带时区参数。这里一定要根据你本机的版本来别直接照抄别人的配置。6.5 中文乱码问题Java Web开发中中文乱码主要有两个来源。一个是数据库连接串没指定编码解决方案是在url上加characterEncodingutf8另一个是页面提交的POST请求编码问题Spring Boot默认已经处理了请求体和响应的UTF-8编码但如果你用了原生的Servlet代码就需要在代码中手动设置request.setCharacterEncoding(UTF-8)。我把所有能设置UTF-8的地方都设置了再没遇到过乱码。结束语很多人在做毕业设计的时候总是想着要用多新颖的技术、多复杂的架构但实际来看一个逻辑完整、代码风格干净、能跑通全流程的系统就足以拿到很好的成绩。宠物商城这个题目的价值在于它涵盖了Java Web开发中最典型的业务场景把登录鉴权、增删改查、事务处理、分页查询、文件上传这些基础能力全部串起来练了一遍。做完这个项目之后你去看其他电商系统、管理系统会发现核心逻辑惊人的相似。我自己在完成这个项目后最深的一点体会是做项目遇到问题不可怕关键是能说清楚问题的现象、排查过程和最终解决方案这在写论文和答辩时都是非常宝贵的素材。希望这篇分享能帮你少走一些弯路早日把这个系统搞明白也搞出自己的亮点。
阅读完成 · 觉得有帮助?