简介:这份资源是面向计算机及相关专业本科生、研究生的课程设计文档,围绕基于Spring Boot的网上购物商城系统展开,帮助读者理解Java技术、MySQL数据库与B/S结构在实际电商项目中的落地方式。文档完整覆盖需求分析、系统设计、功能实现与系统测试全过程,包含用户、商家、管理员三大模块的注册登录、商品浏览购买、商品信息管理及用户商家管理等内容,并给出E-R图与数据库表结构设计,测试部分从功能、性能、安全三方面展开,可作为课程设计选题、项目开发参考与后续维护扩展的资料。资源包共1个docx文件,约3.8MB,结构清晰便于查阅。目前已有76人学习,适合需要完整电商系统案例与规范文档范式的读者参考。
1. 从一份课设文档到能跑起来的商城:这套 Spring Boot 资源到底值不值得拆
很多同学拿到「基于 Spring Boot 的网上购物商城系统」这类课程设计资源时,第一反应是打开文档翻目录,看到需求分析、E-R 图、用例图、测试方案一应俱全,就觉得稳了。真正动手才发现,文档里写的是「用户下单后跳转支付」,代码里可能连订单状态机都没建;文档说「管理员管理商品分类」,数据库里分类表和外键约束对不上。这套资源的真实价值不在于文档写得多完整,而在于它给了一套能跑通的最小闭环:Java + Spring Boot + MySQL + B/S 结构,用户、商家、管理员三个角色,注册登录、商品浏览、购物车、订单管理这几条核心链路都有对应的实现思路和表结构设计。适合正在做课设、需要快速搭出一个可演示系统的本科生,也适合想拿一个完整案例练手 Spring Boot 增删改查和事务处理的初级开发者。下面我按「先跑通再优化」的顺序,把这份资源拆成能直接抄作业的步骤。
2. 环境搭建与数据库落地:从建表到连上 MySQL
2.1 技术选型为什么是 Spring Boot + MySQL 而不是别的
这套资源选 Spring Boot 不是跟风。课设场景下,时间紧、要演示、老师要看「技术栈是否主流」,Spring Boot 的内嵌 Tomcat 和自动配置能让你少写一堆 XML,把精力放在业务逻辑上。MySQL 作为关系型数据库,表结构清晰,E-R 图能直接映射成建表语句,适合购物车、订单这种强关联场景。B/S 结构意味着你只需要一个浏览器就能演示,不用给老师装客户端。如果你换成 NoSQL 或者前后端分离,课设的复杂度会翻倍,答辩时反而不好解释「为什么用这个」。所以我的建议是:先按资源里的技术栈把系统跑起来,再考虑加 Redis 缓存或者消息队列做优化。
2.2 建库建表:把 E-R 图翻译成可执行的 SQL
资源里的 E-R 图给了商品分类、商家、订单、管理员、商品五个核心实体。我一般会先把它们落成建表语句,注意字段类型和索引。下面这段 SQL 可以直接在 MySQL 里执行,建一个shopping_mall库和几张核心表:
-- 创建数据库,字符集用 utf8mb4 支持 emoji CREATE DATABASE shopping_mall DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE shopping_mall; -- 商品分类表:分类名唯一,避免重复添加 CREATE TABLE category ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(64) NOT NULL UNIQUE COMMENT '分类名称', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB; -- 商家表:账号唯一,余额用 DECIMAL 避免浮点误差 CREATE TABLE merchant ( id BIGINT PRIMARY KEY AUTO_INCREMENT, account VARCHAR(64) NOT NULL UNIQUE COMMENT '登录账号', password VARCHAR(128) NOT NULL COMMENT '加密后的密码', name VARCHAR(64) NOT NULL COMMENT '商家名称', phone VARCHAR(20), email VARCHAR(128), balance DECIMAL(12,2) DEFAULT 0.00 COMMENT '账户余额', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB; -- 商品表:外键关联分类和商家,价格用 DECIMAL CREATE TABLE product ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(128) NOT NULL, category_id BIGINT NOT NULL, merchant_id BIGINT NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT DEFAULT 0 COMMENT '库存', image_url VARCHAR(255), detail TEXT, status TINYINT DEFAULT 1 COMMENT '1上架 0下架', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (category_id) REFERENCES category(id), FOREIGN KEY (merchant_id) REFERENCES merchant(id) ) ENGINE=InnoDB; -- 订单表:状态字段用 TINYINT 表示待支付/已支付/已发货/已完成 CREATE TABLE orders ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(64) NOT NULL UNIQUE COMMENT '订单编号', user_id BIGINT NOT NULL, total_amount DECIMAL(12,2) NOT NULL, status TINYINT DEFAULT 0 COMMENT '0待支付 1已支付 2已发货 3已完成', address VARCHAR(255) NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB;建表时有两个参数要特别注意:utf8mb4是为了支持商品名里的特殊字符,DECIMAL是为了金额计算不丢精度。外键约束在课设阶段建议加上,能帮你提前发现数据不一致的问题。如果后面插入数据报foreign key constraint fails,先检查分类和商家是否已经存在。
2.3 Spring Boot 项目初始化与数据库连接配置
用 IDEA 的 Spring Initializr 建项目,勾选 Spring Web、Spring Data JPA、MySQL Driver、Thymeleaf(如果做服务端渲染)。pom.xml里关键依赖如下:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>application.yml里配置数据源,注意时区和编码:
spring: datasource: url: jdbc:mysql://localhost:3306/shopping_mall?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update # 开发阶段自动建表,生产环境改成 none show-sql: true # 控制台打印 SQL,方便调试ddl-auto: update在课设阶段很好用,实体类改了字段它会自动同步表结构。但要注意:它不会删除字段,只会新增。如果你把某个字段改名,旧字段会留在表里,新字段会加上去,容易造成数据混乱。我一般会在开发初期用create,每次重启清空重建,数据稳定后再改成update。
3. 用户、商家、管理员三端核心功能实现
3.1 注册登录:密码加密与角色区分
资源里注册流程写了「判断用户名是否存在」,登录流程写了「判断用户名和密码是否正确」。实现时最容易翻车的地方是密码明文存储。我一般用 BCrypt 做哈希,Spring Security 的BCryptPasswordEncoder可以直接用,不引入 Security 的话也可以单独加spring-security-crypto依赖。
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; public class PasswordUtil { private static final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); // 注册时调用,返回加密后的密码存库 public static String encode(String rawPassword) { return encoder.encode(rawPassword); } // 登录时调用,比对明文和数据库里的哈希 public static boolean matches(String rawPassword, String encodedPassword) { return encoder.matches(rawPassword, encodedPassword); } }角色区分用一张user表加role字段,或者三张独立表。资源里是用户、商家、管理员分开的,我建议保持这个设计,因为三者的字段差异较大(商家有余额、身份证,用户有收货地址)。登录时根据账号去对应表查询,返回不同的角色标识,前端根据角色跳转到不同首页。这里有个坑:如果三张表都有account字段,登录接口要明确告诉前端「当前登录的是哪个角色」,否则前端不知道跳哪里。
3.2 商品浏览与购物车:分页查询和 Session 存储
商品列表要分页,Spring Data JPA 的Pageable可以直接用:
@GetMapping("/products") public Page<Product> listProducts( @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(required = false) Long categoryId) { Pageable pageable = PageRequest.of(page, size, Sort.by("createTime").descending()); if (categoryId != null) { return productRepository.findByCategoryId(categoryId, pageable); } return productRepository.findAll(pageable); }购物车在课设阶段不建议存数据库,用 Session 或 Redis 更简单。Session 方案:HttpSession里放一个Map<Long, Integer>,key 是商品 ID,value 是数量。加入购物车时判断商品是否下架、库存是否足够。这里有个血泪经验:如果用户没登录就加购,Session 里的购物车在登录后会丢失。常见做法是登录成功后把匿名 Session 的购物车合并到用户购物车,或者干脆要求登录后才能加购,课设演示时后者更省事。
3.3 订单创建与状态流转:事务和库存扣减
下单是最容易出问题的地方。用户点击「提交订单」时,要做三件事:扣库存、生成订单、清空购物车。这三步必须在一个事务里,否则扣了库存没生成订单,或者订单生成了库存没扣,都会导致数据不一致。
@Service public class OrderService { @Autowired private ProductRepository productRepository; @Autowired private OrderRepository orderRepository; @Transactional(rollbackFor = Exception.class) public Order createOrder(Long userId, List<OrderItem> items, String address) { BigDecimal total = BigDecimal.ZERO; for (OrderItem item : items) { // 悲观锁:防止并发下单超卖 Product product = productRepository.findByIdForUpdate(item.getProductId()); if (product.getStock() < item.getQuantity()) { throw new RuntimeException("库存不足:" + product.getName()); } product.setStock(product.getStock() - item.getQuantity()); productRepository.save(product); total = total.add(product.getPrice().multiply(BigDecimal.valueOf(item.getQuantity()))); } Order order = new Order(); order.setOrderNo(generateOrderNo()); order.setUserId(userId); order.setTotalAmount(total); order.setAddress(address); order.setStatus(0); // 待支付 return orderRepository.save(order); } }@Transactional(rollbackFor = Exception.class)里的rollbackFor很关键,默认只回滚RuntimeException,如果你抛的是受检异常,事务不会回滚。findByIdForUpdate需要在 Repository 里用@Lock(LockModeType.PESSIMISTIC_WRITE)注解,这样并发下单时第二个请求会等第一个完成,避免超卖。课设演示时并发量不大,但加上这个能让你在答辩时多一个亮点。
4. 避坑与排查:课设系统最容易翻车的五个地方
4.1 中文乱码:从数据库到页面的全链路排查
现象:商品名或地址里的中文在页面上显示成问号或乱码。原因通常是数据库字符集不是utf8mb4,或者 JDBC URL 没加characterEncoding=utf8。解决:先执行SHOW VARIABLES LIKE 'character%';确认character_set_database是utf8mb4,不是的话用ALTER DATABASE shopping_mall CHARACTER SET utf8mb4;修改。然后检查application.yml里的 URL 参数,确保有useUnicode=true&characterEncoding=utf8。最后检查 Thymeleaf 模板的<meta charset="UTF-8">。
4.2 外键约束导致删不掉数据
现象:删除商品分类时报Cannot delete or update a parent row: a foreign key constraint fails。原因是有商品关联了这个分类。解决:要么先删除关联商品,要么在删除分类时把商品的category_id置空或移到默认分类。课设阶段我一般会在 Service 层做检查,如果分类下有商品,直接返回「该分类下还有商品,不能删除」,而不是让数据库抛异常。
4.3 订单状态和支付状态混在一起
现象:用户支付后订单状态没变,或者管理员看到的状态和用户看到的不一致。原因是把「订单状态」和「支付状态」塞进了一个字段。解决:拆成两个字段,order_status表示待支付/已支付/已发货/已完成,pay_status表示未支付/已支付/已退款。这样支付回调只改pay_status,发货只改order_status,逻辑清晰。资源里的订单实体图只画了一个状态字段,实际实现时建议拆开。
4.4 Session 丢失导致购物车清空
现象:用户登录后购物车里的商品没了,或者刷新页面后购物车空了。原因是 Session 超时或者服务重启。解决:开发阶段把server.servlet.session.timeout设长一点,比如3600s。如果用了 Nginx 做负载均衡,还要考虑 Session 共享,课设阶段一般不会涉及,但如果你在本地起了多个实例,记得用 Redis 存 Session。
4.5 测试用例只测了正常流程
现象:答辩时老师问「如果用户没登录直接访问订单页面会怎样」,你发现系统报 500 错误。原因是只写了正常流程的测试,没测边界。解决:至少补三类测试——未登录访问受保护页面、库存为 0 时下单、订单金额和商品总价不一致。Spring Boot 里可以用MockMvc写集成测试,也可以手动在浏览器里开无痕窗口模拟未登录状态。
5. 从能跑到能答辩:三个进阶技巧和验证方法
5.1 用统一返回体包装接口,前端处理更省心
课设系统里 Controller 返回的数据格式五花八门,有的返回String,有的返回Map,前端处理起来很乱。我一般会定义一个Result<T>类:
public class Result<T> { private int code; // 200成功,500失败 private String msg; private T data; public static <T> Result<T> success(T data) { Result<T> r = new Result<>(); r.code = 200; r.msg = "操作成功"; r.data = data; return r; } public static <T> Result<T> fail(String msg) { Result<T> r = new Result<>(); r.code = 500; r.msg = msg; return r; } }Controller 里统一返回Result.success(...)或Result.fail(...),前端只需要判断code是否为 200。这样异常处理也统一了,用@ControllerAdvice捕获全局异常,返回Result.fail(e.getMessage()),避免把堆栈信息暴露给用户。
5.2 用 Postman 或 curl 做接口冒烟测试
答辩前我会把核心接口用 curl 跑一遍,确认没有 500 错误。比如测试商品列表分页:
# 测试商品列表第一页,每页5条 curl -X GET "http://localhost:8080/products?page=0&size=5" -H "Accept: application/json" # 测试登录接口,注意密码要传明文,后端会做哈希比对 curl -X POST "http://localhost:8080/login" \ -H "Content-Type: application/json" \ -d '{"account":"testuser","password":"123456","role":"user"}'如果返回{"code":200,"msg":"操作成功","data":{...}}就说明接口通了。如果返回 500,去看控制台日志,通常是数据库连接失败或者实体映射错误。这个习惯能帮你在答辩前发现 80% 的低级问题。
5.3 数据库备份和初始化脚本
课设演示前一定要把数据库导出成 SQL 文件,万一演示时数据被改乱了,可以快速恢复。用mysqldump命令:
mysqldump -u root -p shopping_mall > shopping_mall_backup.sql恢复时用source命令:
mysql -u root -p shopping_mall < shopping_mall_backup.sql我还会在项目里放一个init.sql,包含建库建表和几条测试数据,这样换一台电脑也能快速把环境搭起来。测试数据至少要有:两个分类、两个商家、每个商家三个商品、一个测试用户、一条已完成订单。这样演示时每个页面都有数据,不会出现空白页。
从那以后我每次拿到课设资源,都强制自己先跑一遍「建库 → 启动 → 注册 → 下单 → 查订单」这条最小闭环,确认能跑通再去看文档里的细节。希望帮到你。
本文还有配套的精品资源,点击获取