☰
Java宠物商城毕设实战:Spring Boot+MySQL+数据可视化
2026/10/7 4:56:22 网站建设 项目流程

每到毕业设计开题那阵子,我后台收到最多的私信,就是关于《Java宠物用品商城系统》这类项目的。很多人不是没选型思路,而是卡在三件事上:功能不知道该拆多细,数据库表不会设计,代码拿到手又不敢在答辩时说那是自己写的。这篇文章我就以 0307 这个版本的宠物用品商城系统为例,从课题选型、技术栈搭配、表结构设计,到登录、下单、支付、数据可视化、爬虫采集、部署和答辩,完整过一遍。适合正在做计算机毕业设计、想用 Java 写一个能装进简历的项目,或者学 Spring Boot 但缺一个完整案例的人参考。内容都是实际跑这套系统时验证过的做法,该避的坑我直接讲。

1. 为什么宠物商城系统值得选:从开题到答辩的判断逻辑

1.1 “商城类”课题为什么不过时

很多学生一听商城系统就摇头,觉得太普通,没有新意。但普通和空洞是两回事。图书借阅、酒店房间管理这类纯 CRUD 项目,功能确实太薄,答辩时老师问一句“你的系统到底解决了什么业务难题”,基本答不上来。而商城天然带着商品、购物车、订单、支付、库存、权限这些真实业务场景,任何一个点都能延伸出值得讨论的话题。

宠物用品商城再把“宠物”作为切口,比泛泛的“网上商城”多了一个故事可讲:身边养猫养狗的人越来越多,宠物主愿意为宠物花钱,市场数据一直往上走。开题报告里的“选题背景和意义”这部分,素材很好找,而且不假大空。从开发量看,它的规模也适中,核心代码大概两三千行,页面再打磨打磨,一个基础不错的本科生在毕业设计周期内是能完成的。基础弱一些,用现成源码和模板调整,也能比较快地跑通。

这个题目还有一个很关键的优点:扩展点多。数据可视化可以单独做成模块,把订单、用户、商品数据变成图表;爬虫可以做成数据采集模块,给商城填初始商品信息;小程序端可以复用同一套后端 API。这意味着一个题目能同时覆盖好几门课的内容,课程设计、毕业设计、求职作品都能用。

1.2 功能拆解:用户端、管理端、统计端

系统按角色拆,逻辑最清楚。用户端给普通消费者用,包括注册登录、商品分类浏览、关键词搜索、商品详情、加入购物车、收货地址管理、提交订单、模拟支付、订单查询和取消、商品评价。管理端给运营人员用,包括管理员登录、商品上下架、库存调整、类目管理、用户管理、订单发货和取消。还有一个统计端,通常放在管理后台里,展示销售额趋势、分类销量、用户增长这些图表,也就是数据可视化模块。

功能这么列完,你会发现它完全是照着真实电商的主流程做的,只是砍掉了秒杀、优惠券这些复杂玩法。对毕业设计来说,主流程完整比功能多更重要。演示的时候,从注册到下单再到支付、发货、评价,一条链路走下来,老师就能判断出你是真的理解业务,而不是拼功能数量。

1.3 同一套业务模型的多语言版本怎么理解

标题里写了 JAVA、PHP、爬虫、APP、小程序、C#、C++、python、数据可视化,很容易让人误以为“一份代码同时支持这么多语言”。第一次见到这套课题的同学经常这样想,然后就慌了。实际不是这样。

这套系统的业务模型是通用的,后端可以用不同语言重写,Java 是一种版本,Python Flask 可以写一个版本,PHP、C#、C++ 也可以做对应的课程设计实现;前端可以是网页,也可以是小程序或 APP;数据可视化可以嵌在管理后台,也可以单独用 ECharts 做页面;爬虫模块则通常用 Python 写,给系统采集初始数据和价格参考信息。

我的建议是主线只挑一种语言,推荐 Java,把它啃透,其他版本作为延伸练习或课程设计的对照。千万别试图八种语言全部写一遍,那是在给自己挖坑。毕业设计的评判标准从来不是语言种类多,而是你有没有把一个项目真正讲清楚。

2. Spring Boot 后端与 MySQL 表结构设计:打好地基再写代码

2.1 技术选型与选型理由

这套系统常见的成熟组合是这样的:

层级选型主要理由
后端框架Spring Boot 2.7 + Spring MVC简化配置,内置 Tomcat,联合开发效率高
持久层MyBatis-Plus 3.5单表 CRUD 不写 SQL,联表查询用注解
鉴权JWT + Spring 拦截器前后端分离场景下,Token 无状态好扩展
缓存Redis存 Token、购物车、首页热点数据
数据库MySQL 5.7 / 8.0成熟稳定,毕业设计完全够用
前端Vue 3 + Vite + Element Plus组件化开发,管理后台 UI 现成
图表ECharts数据可视化标配,文档和踩坑案例多

选 MyBatis-Plus 是因为它的单表查询能力非常适合毕设节奏,你不需要为了一个查询接口写大量 XML。选 JWT 而不是 Session,是因为项目要做成前后端分离,JWT 天然无状态,后端不用维护 Session,Redis 里再存一份还可以做到主动失效。选 Vue 3 是因为生态成熟,Element Plus 能把后台管理页面快速搭出来。这里我要提个醒:微服务、消息队列、分布式事务这些技术在毕业设计阶段不建议硬上。单体架构把事务和业务逻辑讲清楚,已经是合格的水平,盲目引入分布式只会增加演示挂掉的风险。

2.2 核心表结构:字段这样定,后面不返工

数据库设计是这套系统能不能做顺的关键。常见的核心表有这么几张:

表名用途关键字段
t_user用户表id, username, password, nickname, phone, role, status, create_time
t_category类目表id, name, parent_id, sort, status
t_product商品表id, category_id, name, subtitle, price, original_price, stock, sales, image, detail
t_cart购物车表id, user_id, product_id, quantity, checked
t_address收货地址表id, user_id, receiver, phone, region, detail, is_default
t_order订单表id, order_no, user_id, total_amount, pay_amount, pay_type, status, address_snapshot, create_time
t_order_item订单明细表id, order_id, product_id, product_name, price, quantity, subtotal
t_review评价表id, user_id, order_id, product_id, content, rating, create_time

设计时有几个点特别容易踩坑。金额字段一律用 decimal,千万别用 float,不然算总额会出现 0.1+0.2 不等于 0.3 这种问题。订单表里要放一个 address_snapshot 字段,也就是下单那一刻把收货地址整体存进去,因为用户地址表是可以随时改的,历史订单必须保留下单时的地址快照,否则后续查订单会显示成新地址。订单状态建议用 int 枚举,比如 100 待支付、200 已支付、300 已发货、400 已收货、500 已完成,后面想加状态不容易破坏已有逻辑。

初始化 SQL 脚本里最好预置一个管理员账号和一批宠物用品数据,不然第一次启动系统,前端页面空荡荡,演示效果很差。参考建表语句可以写成这样:

CREATE TABLE t_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL COMMENT '订单号', user_id BIGINT NOT NULL, total_amount DECIMAL(10,2) NOT NULL COMMENT '商品总额', pay_amount DECIMAL(10,2) NOT NULL COMMENT '实付金额', status TINYINT NOT NULL DEFAULT 100 COMMENT '100待支付 200已支付 300已发货 400已收货 500已完成', address_snapshot VARCHAR(500) DEFAULT NULL COMMENT '收货地址快照', pay_type TINYINT DEFAULT 1 COMMENT '支付方式', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, pay_time DATETIME DEFAULT NULL, ship_time DATETIME DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2.3 前端工程怎么组织

前端如果用的是 Vue 3 + Vite,目录结构大致是这样的:src/api 放所有接口请求,src/router 配置路由和登录守卫,src/views 放页面,src/store 用 Pinia 管理登录用户状态,src/components 放通用组件。后端接口按模块前缀分组:/api/user 管用户,/api/product 管商品,/api/cart 管购物车,/api/order 管订单,/api/admin 管后台。

前后端联调时统一返回结构非常重要,我建议所有接口都返回这样的格式:{ "code": 200, "message": "ok", "data": {} }。前端 Axios 拦截器统一判断 code,等于 401 就跳转登录页,不等于 200 就弹错误提示。这样页面代码里就不用每个请求都写一遍错误处理,逻辑干净很多。

3. 从登录到支付:核心业务链路的代码实现

3.1 JWT 登录与权限控制的实现

登录流程不复杂,但要注意几个细节。用户输入用户名密码,后端去 t_user 表查用户,密码用 BCrypt 校验,登录成功签发 JWT 返回给前端,前端把 Token 存到本地,后面每个请求都在请求头里带Authorization: Bearer token。后端写一个拦截器统一解析 Token,把当前用户信息放进 ThreadLocal,业务代码里直接用。管理员接口再校验一次角色就行。

密码加密这件事一定要做,不能把明文密码存数据库。BCrypt 每次加密结果都不一样,查库比对用BCrypt.checkpw,这个工具类 Spring Security 里自带,单独引入也行。核心代码大概是这样的:

public Result<UserVO> login(@RequestBody LoginDTO dto) { User user = userService.findByUsername(dto.getUsername()); if (user == null || !BCrypt.checkpw(dto.getPassword(), user.getPassword())) { return Result.error("用户名或密码错误"); } String token = JwtUtil.createToken(user.getId(), user.getRole()); return Result.ok(new UserVO(user, token)); }

JWT 生成时,把 userId 放进 subject,角色放进 claim,过期时间设为 7 天。拦截器解析失败直接返回 401,前端拦截器收到 401 就跳登录页。这样一套下来,登录状态管理就很完整了。

3.2 商品搜索、购物车与地址管理

商品搜索主要用 MyBatis-Plus 的 LambdaQueryWrapper,支持按关键词模糊搜索、按分类过滤、按价格排序。代码很直白:

LambdaQueryWrapper<Product> wrapper = new LambdaQueryWrapper<>(); wrapper.like(StringUtils.hasText(keyword), Product::getName, keyword) .eq(categoryId != null, Product::getCategoryId, categoryId) .orderByDesc(Product::getId);

购物车有两种实现方案。简单方案是直接建一张 t_cart 表,增删改查都落库;复杂方案是用 Redis 的 Hash 结构存。我建议毕业设计先用表存,业务逻辑暴露得清楚,答辩也好讲。如果想拿 Redis 加分,可以把“购物车数量”或“首页商品缓存”放到 Redis 里,效果是一样的,但能把 Redis 的使用讲出来。

地址管理就是典型的 CRUD,下单时会读取默认地址。要注意的是下单不能直接引用地址表的记录,而是把地址内容快照到订单表,这个我在表设计里已经说过,代码层面要在生成订单那一瞬间复制字段,不要把地址表的主键直接存进去当外键用。

3.3 下单、扣库存、模拟支付:事务是重点

下单是整个系统里含金量最高的地方。点击“去结算”后,后端一次性接收购物车选中的商品列表,然后做四件事:校验商品状态和库存,条件更新扣库存,生成订单主表,生成订单明细,清空购物车。这四步必须在一个事务里,任何一步失败都得回滚。

扣库存的核心不是先查再减,而是用一条条件更新 SQL,这是防止超卖的重点:

UPDATE t_product SET stock = stock - #{count} WHERE id = #{productId} AND stock >= #{count}

这条 SQL 利用数据库行锁保证原子性。并发情况下两个请求同时读到库存只剩 1,如果先查再减,两个请求都会通过,库存就变负数了。条件更新会检查stock >= count,不满足就更新 0 行,代码里判断受影响行数,为 0 就直接抛出“库存不足”异常。整个方法加上@Transactional,任何一步失败,前面扣的库存也会跟着回滚。

订单生成后状态是待支付。模拟支付的思路是:后端生成一个支付二维码或直接返回支付成功按钮,前端点击后调模拟支付回调接口,后端把订单状态从待支付改成已支付,记录支付时间。这里必须做幂等处理:回调进来先查订单状态,如果不是待支付就直接返回成功,防止重复通知把已支付订单状态打乱。取消订单时要把库存加回去,同样在一个事务里完成。

3.4 前端联调与统一响应处理

前端封装一个 request.js,Axios 拦截器里统一处理返回结构和错误码。这样页面里只关心数据,不用每个接口都判断成功失败:

axios.interceptors.response.use( res => { const { code, data, message } = res.data; if (code === 401) { router.push('/login'); return Promise.reject(message); } if (code !== 200) { ElMessage.error(message); return Promise.reject(message); } return data; }, error => { ElMessage.error('网络异常,请稍后重试'); return Promise.reject(error); } );

这套封装在很多商城项目里是通用的,理解了它,以后做别的前后端分离项目也能无缝迁移。

4. 给商城加数据可视化和爬虫采集:把毕设从及格拉到良好

4.1 统计数据的聚合 SQL 怎么写

数据可视化本质上是把订单、用户、商品的数据聚合成图表。后端不需要写太复杂的东西,重点是几条统计 SQL。最常用的三个场景是:月度销售趋势、分类销量排行、每日新增用户。

月度销售趋势可以这样查:

SELECT DATE_FORMAT(create_time, '%Y-%m') AS month, COUNT(*) AS order_count, SUM(pay_amount) AS sales_amount FROM t_order WHERE status IN (200, 300, 400, 500) GROUP BY month ORDER BY month;

注意这里用的是 pay_amount,不是 total_amount,因为只有已支付的订单才算真实销售。分类销量排行用订单明细关联商品和类目:

SELECT c.name AS category_name, SUM(oi.quantity) AS sale_count FROM t_order_item oi JOIN t_product p ON oi.product_id = p.id JOIN t_category c ON p.category_id = c.id GROUP BY c.id ORDER BY sale_count DESC LIMIT 10;

每日新增用户更简单,对 t_user 的 create_time 做日期分组就行。这三条 SQL 基本能撑起整个统计看板。

4.2 后端统计接口与 ECharts 页面

后端接口返回 List<Map<String, Object>>,前端拿到数据后塞给 ECharts。比如折线图只需要两个数组,一个月份,一个销售额:

option = { xAxis: { type: 'category', data: months }, yAxis: { type: 'value' }, series: [{ name: '销售额', type: 'line', smooth: true, data: amounts }] };

实际做的时候,统计看板通常放在管理后台的首页:顶部四个统计卡片显示总用户数、总订单数、总销售额、今日订单量,下方是四张图表,包括月度销售折线图、分类销量柱状图、订单状态饼图、用户增长折线图。整套看板做完,演示效果非常直观,也是答辩时最容易让老师记住的模块。

4.3 爬虫模块的合规做法与实现思路

很多毕设项目的痛点是手工录入商品数据太痛苦,爬虫模块正好用来解决这个问题。它可以采集公开的宠物商品参考信息,比如商品名称、参考价格、图片链接,清洗后写入 MySQL,商城首页就有了真实可看的商品卡片,数据可视化也有素材可以展示。

做这个模块必须守住底线:只采集公开且允许访问的信息,不采集个人数据,不绕过登录和验证机制,遵守目标网站的 robots 规则,请求间隔随机控制在 1 到 3 秒,抓到的数据只用于学习演示,不上线商用。这是合规底线,如果老师问到,你把这个边界讲清楚,反而是加分项。

实现思路很清晰:用 Python 的 requests 请求公开的商品列表接口或页面,再用 XPath 或 BeautifulSoup 解析出商品名、价格、图片链接,清洗后批量写入数据库。也可以用 Java 的 HttpClient 加 Jsoup 实现,效果差不多。关键不是代码多炫,而是能讲出“请求、解析、清洗、入库”四步。千万不要去抓任何需要破解、绕过访问控制的内容,也不要采集用户隐私,这类风险在毕业设计里一点都不能碰。

5. 本地部署与高频报错:从“能跑”到“顺利演示”

5.1 启动步骤:环境准备与顺序

本地把项目跑起来,是拿到源码后第一件要做的事。环境准备清单大概是这样的:JDK 1.8 或 11,Maven 3.6 以上,MySQL 5.7 或 8.0,Redis,Node.js 16 以上。

启动顺序别搞反。先创建数据库并导入 sql 脚本,再改后端配置文件,然后启动 Redis,再启动后端,最后启动前端。后端数据源配置需要注意几个常见的坑:

spring: datasource: url: jdbc:mysql://localhost:3306/pet_shop?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true username: root password: 123456 redis: host: localhost port: 6379

后端启动用mvn spring-boot:run,或者打包后运行mvn package -DskipTests然后java -jar target/xxx.jar。前端安装依赖后执行npm run dev,默认端口一般是 5173,访问就能看到商城首页。

5.2 我见过的高频报错与处理

每次给学弟学妹看这套项目,基本都会碰到这几个问题,我直接列出来:

报错现象根因与解决
Access denied for user用户名密码不对,或 MySQL 8 认证问题,url 加 allowPublicKeyRetrieval=true
Unknown database忘记先创建数据库再导入 sql,先执行 CREATE DATABASE pet_shop
Redis connection refusedRedis 没启动,Windows 上先启动 redis-server,检查 protected-mode 配置
npm install 很慢或失败换国内镜像源,删除 node_modules 后重装
后端启动端口被占用杀掉占用进程,或修改 server.port
前端请求接口 404检查 vite 代理配置,是否把 /api 代理到后端 8080
登录后刷新就掉线检查前端是否每次请求都携带 Authorization 头

这些坑基本覆盖了绝大多数启动阶段的问题。我的经验是,遇到报错先看控制台最下面那几行,不要被前面大段堆栈吓到,大部分问题是配置,不是代码。

5.3 部署到服务器:思路与注意点

如果毕业设计答辩需要现场演示,本地跑就够了。但如果想部署到云服务器上让老师通过链接访问,思路也不复杂。后端用 Maven 打包成 jar,通过nohup java -jar xxx.jar &后台运行。前端执行npm run build生成 dist 目录,再用 Nginx 托管。Nginx 配置里要处理静态文件和 API 反向代理:

server { listen 80; server_name your_domain; root /var/www/pet-shop/dist; index index.html; location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; } location / { try_files $uri $uri/ /index.html; } }

全程演示的稳定性特别重要。我见过不少答辩现场因为本地网络或环境问题翻车,如果有条件,一定要提前在干净环境里完整跑一遍,最好把浏览器和数据库都提前打开,不要现场等启动。

6. 答辩准备和二次开发:把项目真正变成自己的

6.1 老师常问的问题与回答思路

答辩最怕的不是问题难,而是项目不是自己写的,一问就露馅。下面这些问题是老师问得最多的,每一个你都要能用自己的话讲出来:

老师问题回答思路
为什么用 Spring Boot?简化配置,自动装配,内置 Tomcat,适合快速开发单体项目
权限是怎么控制的?前端路由守卫控制页面,后端拦截器解析 JWT,管理员接口再校验角色
库存为什么不会超卖?数据库条件更新扣库存,配合事务保证原子性,不是先查再减
Redis 在项目里存了什么?存登录 Token、购物车数据、首页热点缓存,能主动失效
模拟支付怎么实现的?前端调支付回调接口,后端幂等更新订单状态,对接微信支付宝是生产环境做法
数据可视化数据从哪来?订单表、用户表、商品表按时间聚合查询,ECharts 渲染
爬虫有没有合规风险?只采集公开数据,遵守 robots,控制频率,不绕过访问控制,仅用于毕设演示
订单状态有哪些?待支付、已支付、已发货、已收货、已完成,取消订单要恢复库存
如果用户量大,瓶颈在哪?单库单表、数据库并发瓶颈,可以加缓存、分库分表、异步消息队列
哪些代码是你写的?一定要诚实,把核心模块的代码逻辑自己过一遍再说

回答的时候有个技巧:讲到任何功能都顺手带一句“为什么这么设计”。比如讲库存,就说“因为这个操作要面对并发场景,所以用了条件更新而不是先查再减”。老师听到的是思考过程,而不仅仅是结果。

6.2 值得做的二次开发方向

基础功能做完之后,如果想进一步提升含金量,可以从这几个方向里挑一两个,不要贪多。订单超时自动取消,用 Spring 的 @Scheduled 定时扫描待支付订单,超时后自动关闭并恢复库存,这是一个非常实用的功能。优惠券系统,涉及用户领券、下单时校验、扣减,逻辑完整度高。微信支付沙箱对接,走一遍真实的支付流程,比模拟支付更有说服力。小程序端复用后端 API,前端换一套小程序框架,工作量可控,展示效果却很好。商品多图轮播、物流单号查询、热门搜索词统计,这些是锦上添花。

6.3 源码拿到手后的使用流程

最后分享一点实际经验。不管这套 0307 版本的源码是从哪个渠道拿到的,都别急着改成花里胡哨的页面。第一步永远是先跑起来,把数据库账号改成自己的,把 Redis 启起来,前后端能通,再谈别的。第二步是全局搜索默认关键词,把标题、页面里的项目名、系统名都改成自己的。第三步最重要:通读核心代码,尤其是登录、下单、扣库存、统计 SQL 这几块,每行都弄明白。第四步是用自己的话写项目文档和答辩 PPT。“全套文案”可以辅助参考,但里面涉及技术方案、功能列表的内容,一定要对照自己实际的项目改一遍,不要原样照抄。

我一直觉得,毕业设计最大的价值不在于源码本身,而在于你能把项目讲成一个完整的故事。系统是别人的模板,还是你自己的作品,差别就在最后这个阶段:跑通、改透、说清。这套宠物商城做完,Java、数据库、前后端联调、数据可视化、爬虫这些点你都能在面试里聊上几句,那它就不只是一个毕业设计,而是一份能带走的项目经验。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询