ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Java毕设实战:SpringBoot网上服装销售系统全栈开发指南

Java毕设实战:SpringBoot网上服装销售系统全栈开发指南

简介:本资源是一套完整的Java Web毕业设计项目——网上服装销售系统,基于SSM(Spring+SpringMVC+MyBatis)框架开发,面向计算机专业本科生及Java初学者,用于课程设计、毕业论文实践与Web全栈能力训练。系统采用前后端分离式结构,包含管理员与普通用户双角色权限体系,覆盖商品管理、订单处理、分类维护、新闻资讯、购物车等电商核心模块,具备可运行、可调试、可二次开发的工程完整性。压缩包共1302个文件,主体为142个JSP页面、110个Java后端类、358个JS交互脚本、165个CSS样式文件及大量PNG/GIF/JPG静态资源,总大小15.94MB,前端集成Bootstrap、Layui、ElementUI等主流UI库,风格统一且适配度高。目前已有176人学习下载,提供完整源码、数据库SQL脚本、详细目录结构与模块注释,开箱即用,适合快速理解电商系统业务逻辑、SSM整合流程与前后端协同开发范式。

1. 为什么一个“网上服装销售系统”毕业设计,能帮你把 Java 基础、SpringBoot 工程能力、数据库建模和前后端联调全串起来?

不是所有 Java 毕业设计都值得花三个月实打实写——但「网上服装销售系统」是少有的、天然自带完整业务闭环的选题:用户注册登录 → 浏览商品(分类/搜索/详情)→ 加入购物车 → 提交订单 → 支付模拟 → 订单管理 → 后台商品/库存/订单审核。它不依赖外部支付接口(可本地模拟),不强求高并发(单机 MySQL 完全够用),但每一步都踩在 Java Web 开发的核心链路上:Servlet 生命周期、Spring MVC 请求流转、MyBatis 动态 SQL、事务传播行为、Thymeleaf 模板渲染、RESTful 接口设计、甚至文件上传(商品图)。我带过 17 届到 23 届近 40 个本科毕设,凡是把这套系统从零跑通、能讲清「为什么用 SpringBoot 而不用纯 Servlet」「购物车存 Session 还是 Redis」「订单状态机怎么防重复提交」的同学,Java 面试时基本能稳过技术初面。它不是炫技项目,而是你 Java 工程能力的「压力测试仪」:代码结构是否分层清晰?异常是否统一处理?日志是否能定位问题?这些细节,比写满一万行代码更能说明你的真实水平。


2. 用 SpringBoot 3.2 + MyBatis-Plus 快速搭起骨架:5 分钟初始化一个可运行的后台工程

2.1 创建工程:别再手动下载 jar 包,用官方脚手架生成最小可行项目

直接访问 https://start.spring.io (注意:这是 Spring 官方在线生成器,非第三方平台),按以下配置勾选:

  • Project: Maven
  • Language: Java
  • Spring Boot:3.2.12(当前稳定版,兼容 JDK 17+,避免选 3.3.x 因部分 starter 尚未适配)
  • Packaging: Jar
  • Java: 17(毕业设计环境统一用 JDK 17,避免java: 警告: 源发行版 17 需要目标发行版 17类报错)
  • Dependencies:
    • Spring Web
    • Spring Data JDBC(替代 JPA,更贴近 MyBatis 思维)
    • Lombok(省去 getter/setter,毕设代码整洁度直线上升)
    • Validation(表单校验刚需)
    • Thymeleaf(服务端渲染,比纯 Vue 更适合毕设快速出效果)

提示:不要勾选 Spring Security。毕设阶段先实现基础登录逻辑(用户名密码查库比对),安全框架会陡增复杂度,等核心流程跑通再加。

生成后解压,用 IDEA 打开,确认pom.xml中spring-boot-starter-web版本为3.2.12,且无spring-boot-starter-tomcat冲突依赖(如有,删掉<scope>provided</scope>行)。

2.2 集成 MyBatis-Plus:比原生 MyBatis 少写 70% XML,但保留 SQL 可控性

在pom.xml中追加:

<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-spring-boot3-starter</artifactId> <version>3.5.5</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency>

在application.yml中配置数据库连接(本地 MySQL 8.0):

spring: datasource: url: jdbc:mysql://localhost:3306/clothes_shop?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印 SQL,调试神器 global-config: db-config: id-type: assign_id # 主键用雪花算法,避免自增 ID 暴露业务量

创建实体类User.java(Lombok 简化):

import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import lombok.Data; @Data public class User { @TableId(type = IdType.ASSIGN_ID) // 自动生成 19 位 Long ID private Long id; private String username; private String password; // 明文存储仅用于毕设演示,实际必须 BCrypt 加密 private String phone; private Integer status; // 0-禁用, 1-启用 }

创建 Mapper 接口(无需 XML):

import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; @Mapper public interface UserMapper extends BaseMapper<User> { // 继承 BaseMapper 后,selectById、insert、updateById 等方法自动可用 }

写一个测试 Controller 验证连通性:

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class TestController { @Autowired private UserMapper userMapper; @GetMapping("/test-db") public String testDb() { return "DB OK, user count: " + userMapper.selectCount(null); } }

启动项目,访问http://localhost:8080/test-db,返回类似DB OK, user count: 0即表示数据库已打通。这步是毕设生死线:如果卡在这里,90% 是 MySQL 密码错误、时区配置缺失或驱动版本不匹配。


3. 商品与订单核心模块:用三层架构拆解业务逻辑,避免 Service 层变成“上帝类”

3.1 数据库设计:聚焦服装行业特性,避开电商通用模型的过度抽象

毕设不需要照搬淘宝的千万级表结构。我们只建 5 张核心表,字段精简但覆盖关键业务:

表名关键字段说明
t_userid,username,password,phone,create_time用户基础信息,密码明文仅限毕设
t_categoryid,name,parent_id,sort_order服装分类(男装/女装/童装 → T恤/牛仔裤/连衣裙),支持两级分类
t_productid,name,price,stock,category_id,image_url,detail商品主表,image_url存相对路径如/upload/20240510/tshirt1.jpg
t_cartid,user_id,product_id,quantity,create_time购物车,用数据库存而非 Session,方便多端同步
t_orderid,order_no,user_id,total_amount,status,create_time订单主表,status用整数:1-待支付, 2-已支付, 3-已发货, 4-已完成
t_order_itemid,order_id,product_id,quantity,price订单明细,记录下单时快照价格

注意:t_order.order_no不用 UUID(太长),用时间戳+用户ID+随机数生成,例如20240510123456789012345(15位时间+10位用户ID+3位随机),保证唯一且可读。

3.2 实现购物车功能:为什么用数据库存比 Session 更适合毕设验证

很多同学用HttpSession存购物车,导致重启服务后购物车消失,答辩时被问“用户换设备还能看到购物车吗?”当场哑火。毕设要体现工程思维,不是堆功能。我们用数据库存,逻辑清晰:

  1. 用户登录后,每次操作购物车前,先查t_cart表获取该用户所有商品;
  2. 添加商品时,检查product_id + user_id是否已存在,存在则quantity += 1,否则insert新记录;
  3. 前端用 AJAX 调用/cart/add?productId=1001&quantity=2,后端 Controller 处理:
@PostMapping("/cart/add") public Result addCart(@RequestParam Long productId, @RequestParam(defaultValue = "1") Integer quantity) { // 1. 校验商品是否存在且有库存 Product product = productService.getById(productId); if (product == null || product.getStock() < quantity) { return Result.fail("商品不存在或库存不足"); } // 2. 查询用户购物车中是否已有该商品 LambdaQueryWrapper<Cart> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(Cart::getUserId, getCurrentUserId()) .eq(Cart::getProductId, productId); Cart cartItem = cartService.getOne(wrapper); if (cartItem != null) { // 已存在,更新数量 cartItem.setQuantity(cartItem.getQuantity() + quantity); cartService.updateById(cartItem); } else { // 不存在,新增 Cart newCart = new Cart(); newCart.setUserId(getCurrentUserId()); newCart.setProductId(productId); newCart.setQuantity(quantity); cartService.save(newCart); } return Result.success("添加成功"); }

关键点:getCurrentUserId()从 Session 中取(session.getAttribute("userId")),Session 只存用户身份,不存业务数据——这是分层清晰的标志。


4. 常见问题排查:毕设最常翻车的 4 个坑,附真实报错和秒级修复方案

4.1 现象:启动时报java.lang.ClassNotFoundException: javax.servlet.Filter

原因:SpringBoot 3.x 默认使用 Jakarta EE 9+ 规范,包名从javax.*升级为jakarta.*,但你的pom.xml里可能残留了老版本依赖(如servlet-api2.5 或jsp-api)。
解决:全局搜索javax.servlet,删除所有含javax.servlet的<dependency>;确保spring-boot-starter-web是 3.2.x 版本(它已内置jakarta.servlet)。若用到了 JSP(不推荐),需改用jakarta.servlet.jsp。

4.2 现象:Thymeleaf 模板中${user.username}显示为空,但后端System.out.println(user)能打印出值

原因:Controller 方法返回String时,Thymeleaf 默认查找templates/xxx.html,但你可能把 HTML 放在了static/目录下(那是静态资源目录,不走模板引擎)。
解决:确认 HTML 文件路径为src/main/resources/templates/index.html,且 Controller 返回"index"(不带.html后缀);检查application.yml是否误删了spring.thymeleaf.prefix: classpath:/templates/(默认存在,一般不用显式配置)。

4.3 现象:MySQL 插入中文乱码,显示为???

原因:MySQL 服务端、数据库、表、连接 URL 四处编码不一致。常见是数据库建库时没指定CHARSET=utf8mb4。
解决:

  1. 登录 MySQL,执行SHOW VARIABLES LIKE 'character_set_%';,确认character_set_server为utf8mb4;
  2. 重建数据库:CREATE DATABASE clothes_shop CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
  3. application.yml中url参数补全:?useUnicode=true&characterEncoding=utf8mb4;
  4. 重启 MySQL 服务(Windows 下服务管理器重启,Linux 下sudo systemctl restart mysql)。

4.4 现象:点击“提交订单”后页面空白,浏览器控制台 Network 显示 500 错误,但 IDEA 控制台无任何日志

原因:@RestController和@Controller混用错误。@RestController默认返回 JSON,若你写了return "order_success";,它会把字符串当 JSON 值返回,前端无法解析;而@Controller需配合@ResponseBody才返回 JSON。
解决:

  • 返回页面跳转:用@Controller+return "order_success";(Thymeleaf 渲染);
  • 返回 JSON 结果:用@RestController+return Result.success(...);;
  • 绝对禁止@RestController方法里return "string",这是毕设答辩高频扣分点。

5. 毕设答辩加分项:用 POI 导出订单报表(含图表),破解“java poi word能生成图表吗”这个高频疑问

5.1 为什么导出 Word 报表比 PDF 更适合毕设展示?

PDF 需要 iText 或 Flying Saucer,配置复杂且中文支持易出错;而 Apache POI 对 Word(.docx)支持成熟,能直接插入表格、文字、甚至折线图,且生成的文件双击即可用 WPS/Office 打开,答辩时老师一眼看懂你的工作量。重点来了:POI确实能生成图表,但不是用 Word 原生图表(那需要 COM 组件,Windows 专属),而是用XWPFDocument插入 PNG 图片——你用 Java 画好图,转成字节数组塞进去。

5.2 三步实现“月度销售统计图”Word 导出

Step 1:添加 POI 依赖

<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>5.2.4</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.4</version> </dependency> <!-- 画图用 --> <dependency> <groupId>org.jfree</groupId> <artifactId>jfreechart</artifactId> <version>1.5.3</version> </dependency>

Step 2:用 JFreeChart 画柱状图,转 PNG 字节数组

import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.data.category.DefaultCategoryDataset; public byte[] generateSalesChart(List<Order> orders) { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); // 按月份聚合销售额(简化:假设 orders 已按月分组) for (Order order : orders) { dataset.addValue(order.getTotalAmount(), "销售额", order.getMonth()); } JFreeChart chart = ChartFactory.createBarChart( "月度销售统计", "月份", "金额(元)", dataset ); // 转 PNG 字节数组 ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { ChartUtils.writeChartAsPNG(baos, chart, 600, 400); } catch (IOException e) { throw new RuntimeException("图表生成失败", e); } return baos.toByteArray(); }

Step 3:插入 Word 文档并下载

@GetMapping("/export/report") public void exportReport(HttpServletResponse response) throws IOException { List<Order> orders = orderService.getMonthlyOrders(); // 自定义查询方法 byte[] chartBytes = generateSalesChart(orders); XWPFDocument doc = new XWPFDocument(); // 插入标题 XWPFParagraph title = doc.createParagraph(); title.setAlignment(ParagraphAlignment.CENTER); XWPFRun titleRun = title.createRun(); titleRun.setText("网上服装销售系统 - 月度销售报告"); titleRun.setFontSize(16); titleRun.setBold(true); // 插入图表 XWPFParagraph chartPara = doc.createParagraph(); XWPFRun chartRun = chartPara.createRun(); chartRun.addPicture( new ByteArrayInputStream(chartBytes), XWPFDocument.PICTURE_TYPE_PNG, "sales_chart.png", Units.toEMU(500), // 宽度 500pt Units.toEMU(300) // 高度 300pt ); // 插入销售明细表格 XWPFTable table = doc.createTable(orders.size() + 1, 4); String[] headers = {"订单号", "用户", "金额", "日期"}; for (int i = 0; i < headers.length; i++) { table.getRow(0).getCell(i).setText(headers[i]); } for (int i = 0; i < orders.size(); i++) { Order o = orders.get(i); table.getRow(i + 1).getCell(0).setText(o.getOrderNo()); table.getRow(i + 1).getCell(1).setText(o.getUsername()); table.getRow(i + 1).getCell(2).setText(String.valueOf(o.getTotalAmount())); table.getRow(i + 1).getCell(3).setText(DateUtil.format(o.getCreateTime())); } // 设置响应头,触发下载 response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document"); response.setHeader("Content-Disposition", "attachment; filename=sales_report_" + System.currentTimeMillis() + ".docx"); doc.write(response.getOutputStream()); doc.close(); }

提示:JFreeChart 生成的图默认黑底白字,若需美化,在ChartFactory.createBarChart后加:

chart.setBackgroundPaint(Color.WHITE); chart.getTitle().setPaint(Color.BLACK);

这个功能在答辩时打开生成的 Word,老师看到带图的报表,会立刻意识到你不仅会 CRUD,还掌握了 Java 生产力工具链。它不增加系统复杂度,却极大提升项目完成度感知——这就是毕设里的“后悔药”。


6. 从毕设到面试:把“网上服装销售系统”转化成 Java 八股文弹药库的 3 个具体动作

6.1 在简历项目描述中,用 STAR 法则替换功能罗列

别写:“实现了用户管理、商品管理、购物车、订单功能”。改成:

  • Situation:本科毕设需独立完成一个具备完整购物流程的 Java Web 系统;
  • Task:解决高耦合导致的维护困难(如订单状态变更散落在多个 Service),并满足答辩时可演示数据一致性;
  • Action:采用 SpringBoot 分层架构,将订单状态流转封装为OrderStatusService,用@Transactional保证库存扣减与订单创建原子性,并通过数据库唯一索引(order_no)防重复提交;
  • Result:系统通过全部业务场景测试,答辩时现场演示并发下单不超卖,获优秀毕设评价。
    这样写,面试官一眼抓住你的工程决策能力,而不是功能清单。

6.2 针对“java怎么保证数据一致性”这类高频面试题,准备一个可复述的毕设案例

库存扣减是经典场景。不要只说“加锁”,要结合你的代码:

“我在OrderService.createOrder()方法上加了@Transactional,并在扣减库存时用UPDATE t_product SET stock = stock - ? WHERE id = ? AND stock >= ?,利用数据库行锁+条件判断,避免超卖。同时在 DAO 层用int rows = productMapper.reduceStock(productId, quantity)返回影响行数,如果rows == 0则抛出InsufficientStockException,由全局异常处理器返回友好提示。这样既利用了数据库 ACID,又避免了分布式锁的复杂度。”

这段话包含事务、SQL 乐观锁、异常处理三层,比背诵“CAS”“AQS”更有说服力。

6.3 把调试过程变成你的“debug 故事集”

面试官爱问:“你遇到最难的 bug 是什么?” 别编造。就讲你毕设里真实的:

“有次购物车数量总是加不对。我先在 Controller 打日志,发现参数quantity是 2,但数据库更新后变成 3。接着查 Service 层,发现cartService.updateById(cartItem)前,cartItem.getQuantity()已是 3。最后定位到前端传参时用了?quantity=2&quantity=1(jQuery serialize 重复绑定),后端@RequestParam默认取最后一个值。我改用@RequestBody CartDTO接收 JSON,彻底解决。”
这个故事展示了你完整的排查链路:现象 → 日志定位 → 代码审查 → 网络抓包 → 根因分析 → 方案落地。这才是工程师的真实能力。

我带过的毕业生里,能把毕设里一个真实 bug 的解决过程讲清楚的人,Java 基础面试通过率超过 90%。因为 debug 能力无法速成,它暴露的是你每天和代码打交道的真实痕迹。希望帮到你。

本文还有配套的精品资源,点击获取

返回列表