SSM超市管理系统课程设计实战:从框架配置到库存扣减完整链路
发布时间:2026/10/13 3:57:02 锦皓数字建站

简介基于SSM框架的超市管理系统课程设计资源面向计算机相关专业学生及初级Java开发人员用于快速掌握Spring、Spring MVC与MyBatis的整合开发并理解超市商品、库存、销售等典型业务场景的实现方式。资源包含完整项目源码、SQL初始化脚本与配置文件功能模块覆盖商品管理、库存管理、销售管理、会员管理和统计报表前端以JSP结合HTML、CSS与JavaScript呈现页面可对照学习前后端交互流程。压缩包共792个文件其中Java与class文件对应后端逻辑XML文件包含MyBatis映射及Spring配置HTML、JSP、CSS与JS文件构成界面展示层SQL文件用于建表与初始化数据另有GIF演示及PNG图片辅助查看运行效果整体约1.78MB目录结构便于按模块检索。目前已有84人学习下载适合课程设计参考、SSM入门实操或作为毕业设计基础框架使用。1. 课程设计选SSM超市管理系统先搞懂它解决的是什么SSM 的超市管理系统在课程设计里出镜率一直不低用 Spring、Spring MVC、MyBatis 把登录、商品管理、进货、收银和库存管理串成一条完整链路。我的经验是这门课设的难点从来不是“代码写不出来”而是启动到一半被 XML 配置拦住、演示时浏览器和数据库的字符集搅成一团、答辩被问两句就说不出话。真正值得投入的是把这个系统做成“能启动、能演示、能讲清楚”的完整闭环而不是把某个源码包改个名字就交掉。这篇文章不给你造神仙架构就按做这个课设最常见的路径把选型、建表、实现、排错、验收讲一遍适合正在选型、以及准备冲刺交付的入门开发者。2. SSM三件套的分工与项目骨架理论选型不虚配置文件能跑2.1 为什么是 SSM三件套各自到底在做什么超市管理系统跑起来之后用户看到的是登录页、商品表格、订单列表但真正能把数据存进 MySQL 并展示出来的是三层框架各管一摊。Spring 负责创建对象和管理依赖Service 类、DAO 类都交给容器管你要用的对象大多通过注入拿到Spring MVC 负责接收浏览器请求决定这个 URL 由哪个 Controller 的哪个方法处理再把返回结果交给 JSP 或者 JSONMyBatis 则负责把 Java 方法和 SQL 语句做映射查询出来的数据变成 Java 对象反过来也能把参数填进 SQL。这三个框架为什么组合起来做课程设计合适我想说两个理由。第一它们的分层足够清楚答辩时你可以顺着“JSP - Controller - Service - Mapper - MySQL”这条线讲老师顺着这条线问你也不会乱第二配置虽然比 Spring Boot 多一点但正是这些 XML 让你能讲清楚“框架是怎么被组装起来的”。如果课程设计不限技术栈我一般还是建议用 SSM而不是直接上 Spring Boot。Spring Boot 确实省事但很多答辩问题都是针对 SSM 配置出的比如 MyBatis 扫不到接口、事务不生效、视图解析器路径写错提前踩一遍反而是好事。下面一张表把这个组合的核心职责说透框架/组件负责的事在你项目里对应的痕迹Spring对象创建、依赖注入、事务管理Service 里的 Autowired、TransactionalSpring MVC请求分发、参数绑定、视图控制Controller、RequestMapping、ResponseBodyMyBatisSQL 映射、结果集转对象Mapper 接口 resources 下的 XML 文件MySQL最终存储与查询数据库名 shop_demo8.0 或 5.7 版本Maven依赖下载、打包部署pom.xml、war 包或内置 Tomcat 插件2.2 Maven 坐标与项目结构照着建不会在第一步翻车课程设计里的 SSM 项目我建议直接用 Maven 的 war 工程。结构上保持单一职责不要图省事把所有 Controller 塞一个类里也不要为省时间去网上复制一个“万能 pom.xml”。我自己习惯的包名根路径是com.example.shop按 controller/service/mapper/entity 分包resources 下放 mapper XML。这样做的最大好处是后面排查问题时你一眼能看出“类在哪个包、XML 在哪个目录”不会出现 Mapper 接口和 XML 到处乱放导致绑定失败的情况。下面是一份我在做超市管理系统这类项目时最常用的一组依赖坐标。版本号不用死记但要注意 Spring、MyBatis、MyBatis-Spring 三者要能兼容这道菜有个常见翻车点就是版本不匹配导致启动时报 NoSuchMethodError。properties maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties dependencies !-- Spring MVC 与 Spring 容器 -- dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version5.2.5.RELEASE/version /dependency !-- Spring 事务与 JDBC 支持 -- dependency groupIdorg.springframework/groupId artifactIdspring-jdbc/artifactId version5.2.5.RELEASE/version /dependency !-- MyBatis 核心 -- dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version3.5.6/version /dependency !-- MyBatis 与 Spring 整合 -- dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version2.0.5/version /dependency !-- MySQL 8.0 驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.11/version /dependency !-- JSON 转换用于提供接口 -- dependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId version2.9.9/version /dependency !-- JSP 需要 Servlet 与 JSTL -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency /dependencies这里有几个参数值得说明。scope为provided的 servlet-api 依赖意味着编译时需要、打包不需要因为 Tomcat 里已经有了不然打包到 Tomcat 的 lib 下会冲突在课程设计里这是个很低级但很常见的报错。spring-jdbc不是可有可无的依赖它提供了transactionManager事务管理器实现类只引入 spring-webmvc 是不够的。MyBatis-Spring 的版本和 MyBatis 版本要匹配如果是 MyBatis 3.5.6配 2.0.5 比较稳这个组合我跑过很多次没有出过兼容性问题。2.3 web.xml 与 Spring 配置让 Tomcat 启动时不报这两类错war 工程要跑起来src/main/webapp/WEB-INF/web.xml是入口。课程设计大多数同学不是忘了加 Spring 的监听器就是没配置字符集过滤器导致页面提交的中文到数据库变成问号。下面这份 web.xml 把 MVC 前端控制器、Spring 容器监听器和字符集过滤器都放进去启动顺序由 web.xml 的加载顺序保证先去初始化 Spring 容器再初始化 Spring MVC 容器。?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd version4.0 !-- 字符集过滤器解决 POST 中文乱码 -- filter filter-nameencodingFilter/filter-name filter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param init-param param-nameforceEncoding/param-name param-valuetrue/param-value /init-param /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping !-- 加载 resources 下的 Spring 主配置 -- context-param param-namecontextConfigLocation/param-name param-valueclasspath:spring/applicationContext.xml/param-value /context-param listener listener-classorg.springframework.web.context.ContextLoaderListener/listener-class /listener !-- Spring MVC 前端控制器 -- servlet servlet-namedispatcher/servlet-name servlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class init-param param-namecontextConfigLocation/param-name param-valueclasspath:spring/spring-mvc.xml/param-value /init-param load-on-startup1/load-on-startup /servlet servlet-mapping servlet-namedispatcher/servlet-name url-pattern//url-pattern /servlet-mapping /web-appDispatcherServlet的映射是/这意味着所有请求都会经过 Spring MVC静态资源要单独放行。另一个关键点是spring-mvc.xml里的包扫描只扫 Controller不要把 Service 和 Mapper 也扫进去否则会创建两份实例事务代理也会变得乱。主容器applicationContext.xml里只写数据源、SqlSessionFactory、Mapper 扫描和事务管理器两个容器各扫各的包这样团队协作时也不用担心互相覆盖 Bean 定义。3. 数据库与核心接口设计从商品到订单把数据链路串起来3.1 七张表覆盖一个小型超市的核心业务超市管理系统不需要一上来就设计三十张表课程设计阶段能把“管理员、商品、进货、销售、会员”这个闭环做出来就已经是完整项目了。我建议按业务模块把表拆成下面这些。admin表放管理员账号category表放商品分类product表放商品信息supplier表记录供应商stock_in表记录入库单member表放会员orders表放销售订单order_item表放订单明细。建表时需要注意几个统一规范这些规矩会让后面写 Mapper 省很多事。主键统一用BIGINT AUTO_INCREMENT金额字段全部用DECIMAL(10,2)绝不使用FLOAT或DOUBLE每张表都带上create_time字段方便以后做统计逻辑删除字段status用TINYINT默认 1 表示有效而不是物理删除数据。下面这份 SQL 可以直接放进 Navicat 或者命令行执行针对 MySQL 8.0 写的。CREATE DATABASE IF NOT EXISTS shop_demo DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE shop_demo; CREATE TABLE admin ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(30) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL COMMENT 建议存 MD5 或 BCrypt, real_name VARCHAR(20), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE category ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(30) NOT NULL, parent_id BIGINT DEFAULT 0, status TINYINT DEFAULT 1, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE product ( id BIGINT PRIMARY KEY AUTO_INCREMENT, category_id BIGINT NOT NULL, name VARCHAR(100) NOT NULL, barcode VARCHAR(32), price DECIMAL(10,2) NOT NULL, cost_price DECIMAL(10,2), stock INT NOT NULL DEFAULT 0, warn_stock INT DEFAULT 10, status TINYINT DEFAULT 1, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_category (category_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE supplier ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, contact_person VARCHAR(20), phone VARCHAR(20), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE stock_in ( id BIGINT PRIMARY KEY AUTO_INCREMENT, product_id BIGINT NOT NULL, supplier_id BIGINT, quantity INT NOT NULL, purchase_price DECIMAL(10,2), operator VARCHAR(20), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE member ( id BIGINT PRIMARY KEY AUTO_INCREMENT, phone VARCHAR(20) NOT NULL UNIQUE, name VARCHAR(20), points INT DEFAULT 0, balance DECIMAL(10,2) DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE orders ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL, member_id BIGINT, total_amount DECIMAL(10,2) NOT NULL, pay_type TINYINT DEFAULT 1 COMMENT 1 现金 2 扫码 3 会员余额, status TINYINT DEFAULT 1, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_order_no (order_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE order_item ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_id BIGINT NOT NULL, product_id BIGINT NOT NULL, product_name VARCHAR(100), price DECIMAL(10,2) NOT NULL, quantity INT NOT NULL, subtotal DECIMAL(10,2) NOT NULL, KEY idx_order (order_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;orders这个表名在 MySQL 里有特殊含义不过加上反引号之后可以绕过去但我还是建议你换一个名字比如sale_order。写 Mapper 的时候凡是数据库保留字比如order、status、desc都要养成加反引号的习惯。另一个值得留意的参数是product表里的warn_stock它表示库存预警阈值演示的时候可以先设置成 10再通过入库把库存改到 5系统列表就出现“库存不足”的红色标记这是个很好的加分演示点。3.2 订单和库存的链路先查库存再扣库存再写明细超市管理系统最核心的业务动作是“下单”。很多课程设计把下单当成简单的 insert 处理其实漏掉了最重要的库存检查。真实逻辑应该是用户提交购买商品编号和数量后系统先从product表查当前库存如果库存小于购买数量直接返回“库存不足”如果库存充足插入一条orders记录拿到订单 ID再往order_item插入明细最后执行库存扣减。这个过程必须包在同一个事务里否则可能出现订单创建了但库存没扣的情况。我一般建议在 Service 层写一个createOrder方法四个步骤用Transactional注解包起来。这样可以避免用两个 Controller 方法分别处理订单和扣库存也能保证断电或异常时数据不会少一半。考虑到课程设计阶段不需要复杂的消息队列和分布式事务这种本地事务已经足够应对并发量不高的超市场景。3.3 三个扩展点供应商、入库、会员积分如果只做最基础的 CRUD答辩很容易被问住。供应商和入库表其实是给“进货管理”准备的数据支撑别小看这两张表它们能帮你把“商品库存是从哪来的”讲明白。stock_in表记录每次入库的商品、数量、采购价和经手人在页面上做完入库操作之后product表里的stock同步增加这就是一个连续的数据流商品 - 入库单 - 库存变化。会员表和积分字段则是典型的加分项。超市给会员积分是合理业务member表里的points字段可以在下单时累计。比如设置每消费 10 元积 1 分用 SQL 就能完成不用引入额外组件。这三个扩展点都指向同一个思路课程设计不要只做增删改查要做一个能自圆其说的业务流程。答辩时老师问“你这个积分有什么用”你可以当场演示一次下单后会员积分变化比空口说“以后可以做营销”要有说服力得多。4. SSM超市管理系统排查笔记绑定失败、时区、小数精度与事务失效4.1 现象一启动报 Invalid bound statement (not found)这个错误是我见过发生率最高的没有之一。现象是 Tomcat 能启动但一访问商品列表接口后台就抛org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)。原因通常是 Mapper 接口的包名和 XML 的 namespace 不一致或者是 XML 文件放错目录根本没被 Maven 编译到target/classes里。解决时先用两步定位第一打开target/classes目录看有没有对应 mapper XML第二核对 XML 根节点的 namespace 是否完整写成了com.example.shop.mapper.ProductMapper。下面是正确写法XML 放src/main/resources/mapper/product-mapper.xml。?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.example.shop.mapper.ProductMapper !-- 多条件动态查询商品列表 -- select idsearchProduct resultTypecom.example.shop.entity.Product SELECT * FROM product where if testname ! null and name ! AND name LIKE CONCAT(%, #{name}, %) /if if testcategoryId ! null AND category_id #{categoryId} /if AND status 1 /where ORDER BY create_time DESC /select /mapper这里有两个参数细节。where标签会自动处理掉第一个条件前面的AND避免你手动加WHERE 11这样 SQL 更干净也能防止动态 SQL 拼接错误。CONCAT(%, #{name}, %)是模糊查询的推荐写法把百分号放在参数里而不是写成%${name}%后者会导致 SQL 注入。菜鸟经常把resultType和parameterType混在一起其实resultType是结果映射类型实体类字段和数据库列名一致时可以直接用不一致才需要写resultMap。4.2 现象二MySQL 8 连接报时区错误或公钥检索失败现象是启动时控制台出现The server time zone value Öйú±ê׼ʱ¼ä is unrecognized或者Public Key Retrieval is not allowed。原因很简单MySQL 8.0 对时区敏感而你的 JDBC URL 里没有显式声明。解决方式是改数据源里的连接串下面是课程设计里稳定的写法。bean iddataSource classorg.springframework.jdbc.datasource.DriverManagerDataSource property namedriverClassName valuecom.mysql.cj.jdbc.Driver/ property nameurl valuejdbc:mysql://localhost:3306/shop_demo?useUnicodetrueamp;characterEncodingutf8amp;serverTimezoneAsia/Shanghaiamp;useSSLfalseamp;allowPublicKeyRetrievaltrue/ property nameusername valueroot/ property namepassword value123456/ /bean这里面serverTimezoneAsia/Shanghai用来解决时区报错allowPublicKeyRetrievaltrue解决 MySQL 8.0 的 SHA-256 密码认证问题。driverClassName也要注意MySQL 8.0 要写com.mysql.cj.jdbc.Driver旧版的com.mysql.jdbc.Driver已经废弃。如果数据库连接是封装在jdbc.properties里的记得 XML 里先加载 properties 文件再用${jdbc.url}引用不然改了 URL 不生效。4.3 现象三金额用 Double 计算后出现一堆小数位商品单价 9.9 元买三件结算时页面显示 29.700000000000003。这不是你算错了是double本身不能精确表示二进制小数Java 里对金额做加减乘除就必然会踩这个精度坑。课程设计如果出现这个答辩时印象分会减不少。正确的做法是全链路都用BigDecimal数据库字段用DECIMAL(10,2)实体类用BigDecimal。BigDecimal price new BigDecimal(9.9); BigDecimal quantity new BigDecimal(3); BigDecimal total price.multiply(quantity).setScale(2, RoundingMode.HALF_UP);setScale(2, RoundingMode.HALF_UP)意思是保留两位小数、四舍五入。注意构造方法传入的是字符串不要直接写new BigDecimal(9.9)那样在构造时就已经产生精度损失等于白操作。如果项目里有美元或人民币以外的场景就把RoundingMode.HALF_UP换成对应的舍入策略超市系统大多数情况就是保留两位。4.4 现象四Transactional 不生效库存照样超卖事务不生效是课程设计里防不胜防的坑。现象是这个方法明明加了 Transactional但模拟两个客户端同时下单库存扣成负数。原因通常有两种第一种启动类或配置里没有加tx:annotation-driven transaction-managertransactionManager/注解完全没有被识别第二种方法被同一个类内部调用比如 OrderService 的 A 方法调用同类 B 方法B 的 Transactional 不会生效。Service public class OrderService { Autowired private OrderMapper orderMapper; Autowired private ProductMapper productMapper; Transactional(rollbackFor Exception.class) public void createOrder(Long productId, Integer quantity) { Product product productMapper.findById(productId); if (product.getStock() quantity) { throw new RuntimeException(库存不足); } // 扣减库存 productMapper.decreaseStock(productId, quantity); // 插入订单这里若失败会触发回滚 orderMapper.insert(...); } }rollbackFor Exception.class要把运行时异常和普通异常都纳入回滚否则默认只回滚 RuntimeException。还有个常见写法问题是把异常try...catch吞掉了事务管理器看不到异常自然不会回滚。我在课程设计里一般要求学生不要吞异常要把异常抛给外层统一处理器处理。4.5 现象五JSP 页面中文乱码页面显示“鍟嗗搧”这种乱字符中文乱码有三个层面必须三处一起设 UTF-8。数据库连接 URL 带characterEncodingutf8和useUnicodetrueweb.xml 里的CharacterEncodingFilter要配forceEncodingtrueJSP 文件顶部写pageEncodingUTF-8和contentTypetext/html; charsetUTF-8。这三层每一层都查一遍不要只看一处改一处。如果数据库建库时就是 latin1那无论怎么改连接串都没用要对数据库重新设置字符集或者重建数据库。乱码这个坑我见过太多人花一晚上在那试过滤器最后发现是 JSP 页面本身没加pageEncoding。所以排查顺序建议是先看浏览器开发者工具里的响应头 charset再看 JSP 声明最后看数据库连接和表字符集。按这个顺序能省下大把时间。5. 联调与管理流程验证登录、分页、下单扣库存一条线走通5.1 把后端跑起来从 Tomcat 启动到访问登录页代码写完之后第一步不是写新功能而是先把项目在本地完整跑一遍冷启动。我用的是内置 Tomcat 插件的方式也可以直接打包 war 放进本地 Tomcat。启动成功后访问地址应该是http://localhost:8080/shop/如果项目 artifactId 是shop默认上下文路径就是/shop。打开登录页用 admin 账号登录这是整个联调的第一关。如果这一步页面报 404优先检查 Spring MVC 视图解析器里配置的前缀后缀是否正确。bean classorg.springframework.web.servlet.view.InternalResourceViewResolver property nameprefix value/WEB-INF/jsp// property namesuffix value.jsp/ /bean上面这个配置表示 Controller 里返回的字符串login会对应WEB-INF/jsp/login.jsp文件。登录成功后跳到/main主菜单里应能看到商品管理、进货管理、订单管理、会员管理这些入口。如果登录后页面空白而控制台没报错八成是 Controller 返回了字符串但清单里没有对应 JSP 文件把文件名和目录名对齐就能解决。5.2 商品列表与分页手写 LIMIT 也能做得很清晰课程设计里分页功能不是必须的但做了之后整个项目的完成度会明显提高。不引入 PageHelper 也没关系手写LIMIT反而更能讲清楚原理。分页接口的 Service 层需要计算start和sizestart (page - 1) * size然后传给 Mapper。下面这个 Mapper 就是典型的分页查询。GetMapping(/api/product/page) ResponseBody public MapString, Object page(RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 8) Integer size, String name) { int start (page - 1) * size; ListProduct list productMapper.page(start, size, name); int total productMapper.count(name); MapString, Object result new HashMap(); result.put(list, list); result.put(total, total); result.put(page, page); result.put(size, size); return result; }接口里RequestParam(defaultValue 1)表示不传页码时默认从第一页开始这个参数在演示时很有用。count方法和page方法必须传一模一样的查询条件否则分页时总记录数会和列表对不上这是所有分页最容易踩的坑。在 Mapper XML 里page方法的 SQL 末尾要写LIMIT #{start}, #{size}MyBatis 里整数参数需要加#{start}直接替换不能写${start}否则有 SQL 注入风险。5.3 演示一条完整业务流从登录到扣库存全部串起来联调不是每个页面看一眼而是从演示者的视角走通一条完整业务流。第一段管理员登录后进入商品管理页新增一个商品“农夫山泉 550ml”单价 2 元库存 100 件第二段进入进货管理页对这款商品入库 50 件核对商品列表库存变成 150第三段模拟收银台前端往订单接口提交商品 ID 和数量后台扣减库存并生成订单。# 模拟用户下单购买商品 id1数量2 curl -X POST http://localhost:8080/shop/api/order/add \ -d productId1quantity2memberId3 \ -H Content-Type: application/x-www-form-urlencoded # 查询该商品当前库存应为 148 curl http://localhost:8080/shop/api/product/detail?id1用curl的好处是能快速验证后端接口逻辑不依赖前端页面。下单成功后再看数据库里的orders、order_item、product三张表如果订单总价和库存扣减都对得上链路就是通的。最后再做一次异常演示把购买数量改成一个很大的数比如库存 50 件却买 100 件接口必须返回“库存不足”且数据库中的订单记录不能增加。这一步能证明你的事务是真实生效的而不是只在页面上做了提示。6. 验收前最后一小时测试用例、演示顺序与代码清理课程设计能不能拿高分一半看实现一半看呈现。很多明明跑通的系统一答辩就卡在数据库没有测试数据、页面交互不流畅、演示顺序混乱上。我给的第一个建议是做一张手工测试用例表不用很复杂列出项目名、操作步骤、期望结果、实际结果、是否通过。比如登录模块写 3 条用例正确账号密码能登录、错误密码提示失败、空参数提示必填库存模块写 2 条用例正常下单扣库存、超量下单触发回滚。这张表放答辩 PPT 最后一页比说十句“我都测过了”都要有效。第二个建议是控制演示顺序。上来不要急着点页面先说三分钟介绍我用了 SSM 三件套其中 Spring 管对象和事务Spring MVC 管请求流转MyBatis 管 SQL 映射数据库用 MySQL 设计了两类业务表。然后打开系统按“登录 - 商品列表 - 新增商品 - 入库 - 下单 - 库存减少 - 会员积分变化”这条线走。演示过程中数据变化要能看见所以测试数据不要全用“测试1、测试2”要准备几组贴近真实超市生活的数据比如“脉动 600ml”、“可口可乐 330ml”、“清风抽纸”。第三个建议是做一轮代码清理这步是很多人的后悔药。打开代码全局搜一遍System.out.println能删就删不能删就换成Logger输出Controller 里不要写超过二十行的业务逻辑业务逻辑要下沉到 Service统一返回结构用Result、errorCode、message、data这样的格式方便后面扩展。别小看这些细节答辩时老师是会翻看源码的看到层层调用的 Clean 代码和看到一段堆在 Controller 里的面条代码观感天差地别。最后我留个自己的习惯交稿前隔一小时做一次冷启动复测重启数据库、清理 Tomcat 缓存、用无痕浏览器打开项目完整走一遍登录和下单。这一步能筛出许多“刚才还能跑”的假象也比依赖记忆里的成功截图靠谱得多。项目完成后如果学有余力把登录密码改成 BCrypt 加密、给商品列表加导出 Excel 功能都是容易出效果的进阶点。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。