ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SpringBoot鲜牛奶订购系统:JavaWeb架构、订单状态机与开发实战

SpringBoot鲜牛奶订购系统:JavaWeb架构、订单状态机与开发实战 简介面向计算机相关专业毕业设计学生提供基于SpringBoot与JavaWeb的鲜牛奶订购系统完整项目包整合源码、论文、任务书、开题报告、数据库脚本及说明文档可用于快速搭建可运行的高分课题案例。压缩包约35.75MB共848个文件覆盖Java后端、Vue前端、JavaScript、CSS样式、HTML页面和SQL数据库脚本同时包含Word文档、文本说明与批处理脚本资料类型齐全目录结构清晰。项目已通过本地编译调试并附有安装、运行、构建的脚本命令能显著降低环境配置与项目启动难度。论文从设计理念、功能结构、技术选型到实现方法展开论述任务书和开题报告帮助理解项目管理流程数据库脚本可直接导入说明文档则对系统模块和开发细节进行补充便于后续扩展和维护。目前已有57人学习/下载适合正在做大作业、毕业设计或希望进行实战练习的Java学习者。1. 鲜牛奶订购系统到底是什么一个 SpringBoot 单体项目值不值得你花时间拿到「SpringBoot 基于 JavaWeb 的鲜牛奶订购系统的设计与实现」这个标题第一反应多半是又是一个课设/毕设风格的项目包。但拆开看它其实是一个特别典型的业务模型——用户、商品、订单、配送、状态流转整个「订购周期配送」的流程可以原样抽出来改改表名和字段就能套用到桶装水、鲜切花、洗衣液这类同样需要定期配送的生意上。如果你手里正好有带源码、论文、说明文档和数据库文档的项目包你真正要做的不是把代码跑通就完事而是搞清楚三件事这套代码的业务数据是怎么组织的、配置文件的坑都在哪、以及把它改造成能接真实业务需要动哪些地方。这篇笔记就按这个顺序讲中间会贴大量可以直接抄的配置和 SQL也把常见的翻车路径一起列出来。2. 从 JavaWeb 到 SpringBoot这个项目改写了传统 JSP 时代的哪几处关键架构标题里同时出现 JavaWeb 和 SpringBoot很多新手会懵这俩不是一回事吗其实 JavaWeb 指的是「用 Java 做 Web 应用」这个大的技术方向而 SpringBoot 是当下实现 JavaWeb 最主流的落地框架。传统 JavaWeb 课设通常是 Servlet JSP JDBC手动 new 数据库连接、手动配置 web.xmlSpringBoot 则把 SpringMVC、内嵌 Tomcat、数据库连接池全部以 starter 依赖的形式塞进自动配置里。这个项目叫「基于 JavaWeb」框架选 SpringBoot本质是保留了经典三层架构的思想但把最折磨人的配置环节大幅压缩了。2.1 传统三层架构在 SpringBoot 里变成了什么一个标准的 JavaWeb 项目无论底层是 SSH 还是 SSM最终都会落到三层架构表现层Controller、业务层Service、数据访问层DAO/Mapper。SpringBoot 没有发明新架构它只是把这三层的「创建方式」改成了注解驱动。表现层RestController 或 Controller方法上用 GetMapping / PostMapping 绑定 URL。业务层Service 标注实现类事务注解 Transactional 加在方法上。数据访问层如果用 MyBatis就是 Mapper 标注的接口SQL 写在 XML 里或直接用注解。在这个鲜牛奶订购系统里常见做法是这样一个请求链路前端页面 → OrderController接收下单参数→ OrderService校验用户、计算金额、扣库存→ OrderMapper操作订单表和明细表。每一层只做自己那一件事出了问题从 Controller 往 Mapper 一层层看日志很快能定位。这里需要特别提醒一点SpringBoot 的自动配置只能帮你把 Bean 串起来但前提是代码结构符合约定。启动类放在com.xxx.milk包下Controller、Service、Mapper 必须在这个包的子包里否则组件扫描扫不到接口直接 404。这是新手最容易踩的第一个坑而且报错信息还不直观经常是「启动成功但访问不到」。2.2 Maven 依赖与启动类为什么「配置少」反而容易踩坑SpringBoot 项目最核心的 Maven 依赖就四个spring-boot-starter-webWeb 容器、mybatis-spring-boot-starterMyBatis 整合、mysql-connector-java数据库驱动、lombok简化实体类。pom.xml 的核心片段大概长这样parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.1/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency /dependencies这段配置里有两个版本陷阱。第一spring-boot-starter-parent 的版本决定了整个依赖体系的版本2.7.x 对应 JDK 8/113.x 对应 JDK 17 起步如果你的 IDEA 里装的是 JDK 8硬启动 3.x 项目会直接报UnsupportedClassVersionError。第二MySQL 驱动从 8.0.31 之后 groupId 从mysql:mysql-connector-java改成了com.mysql:mysql-connector-j如果你手里的项目包还是旧 groupId 且 Maven 仓库里没有对应版本下载依赖时会卡很久。我一般会先把这两个版本问题确认好再动代码能省一晚上的排查时间。启动类本身极其简单SpringBootApplication MapperScan(com.example.milk.mapper) public class MilkOrderApplication { public static void main(String[] args) { SpringApplication.run(MilkOrderApplication.class, args); } }SpringBootApplication 把 Configuration、EnableAutoConfiguration、ComponentScan 三个注解打包在一起MapperScan 用来告诉 MyBatis 去哪儿找 Mapper 接口。这里不要偷懒不写 MapperScan否则每个 Mapper 接口都要单独加 Mapper漏一个项目启动时就报Invalid bound statement或者注入失败。2.3 页面渲染从 JSP 到 Thymeleaf 或 JSON业务代码该怎么摆传统 JavaWeb 用 JSP 直接渲染页面SpringBoot 里 JSP 的支持很别扭需要额外依赖和配置所以这个项目如果带管理后台页面常见选型是 Thymeleaf。它最大的优点是在 HTML 文件里写th:each、th:if这类属性就能循环渲染数据不需要像 JSP 那样写 Java 片段。但我的建议是把页面和接口分开看。登录页、订单列表页这些可以用 Thymeleaf 渲染但所有涉及下单、支付、状态变更的操作都应该走独立的 JSON 接口用 RestController 返回统一格式的数据。原因很简单——你后面一旦想把 jQuery 页面换成 Vue或者想接小程序有独立接口就不用重写业务层。RestController RequestMapping(/api/order) public class OrderController { PostMapping(/create) public Result createOrder(RequestBody OrderCreateDTO dto) { // 参数校验、调用 service、返回统一 Result } }代码说明/api/order/create接收 JSON 参数返回的 Result 是一个包装类通常包含 code、message、data 三个字段。这样设计之后无论前端是谁只要按这个 JSON 格式对接就行。Thymeleaf 页面只负责展示业务逻辑全部沉淀在 Service 层。我经手过的项目里凡是把业务逻辑写在 Controller 里的后期没有一个不加班的。3. 拆开「订购」这个词订单、配送、库存的最小数据模型设计这个项目的交付物里包含数据库文档说明它的设计者对数据模型是认真对待的。订购系统的核心业务域就三个人和角色、牛奶商品、订单与配送。很多新手一上来就设计七八张表结果订单和配送的状态互相矛盾反而把自己绕晕。以下是一个经过验证的最小表结构覆盖了「下单 → 配送 → 完成」的最简闭环后续想扩展促销、会员、退款在这个基础上加表就行不需要推翻重来。3.1 五张核心表用户、品类、订单主表、订单明细、配送记录一张订单之所以要拆成主表和明细表是因为一个订单里可能同时订了巴氏奶和酸奶主表存订单级别的信息谁订的、总价多少、什么状态明细表存每一件商品的信息什么品类、几份、多少钱。这样拆的好处是统计「某种牛奶卖了多少」时直接查明细表不需要去解析主表里的冗余字段。五张表的推荐设计如下表名关键字段职责说明userid, username, password, role, phone, address用户表role 区分普通用户/配送员/管理员milk_categoryid, name, spec, price, stock, period牛奶品类period 表示配送周期如按周/按月ordersid, order_no, user_id, total_amount, status, create_time订单主表一个订单一行order_itemid, order_id, milk_id, quantity, unit_price, subtotal订单明细一个订单多行delivery_recordid, order_id, delivery_date, status, operator_id配送记录按日期记录每次配送情况其中核心建表 SQL 可以这样起步CREATE TABLE orders ( id bigint(20) NOT NULL AUTO_INCREMENT, order_no varchar(32) NOT NULL COMMENT 业务订单号, user_id bigint(20) NOT NULL COMMENT 下单用户ID, total_amount decimal(10,2) NOT NULL COMMENT 订单总金额, status tinyint(4) NOT NULL DEFAULT 0 COMMENT 状态0待支付 1待配送 2配送中 3已完成 4已取消, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_user_id (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单主表; CREATE TABLE order_item ( id bigint(20) NOT NULL AUTO_INCREMENT, order_id bigint(20) NOT NULL COMMENT 所属订单ID, milk_id bigint(20) NOT NULL COMMENT 牛奶品类ID, quantity int(11) NOT NULL COMMENT 数量, unit_price decimal(10,2) NOT NULL COMMENT 下单时单价, subtotal decimal(10,2) NOT NULL COMMENT 小计, PRIMARY KEY (id), KEY idx_order_id (order_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单明细表;参数说明订单金额用 decimal(10,2) 而不是 float/double这是做财务相关功能最基本的常识浮点数在累加比较时会产生精度误差订牛奶单价看着小事算总价和退款时就会出事。order_no 业务订单号要单独加唯一索引不能依赖自增主键——对外暴露的自增 ID 会被同行爬数据而且订单号里通常会编码日期和门店信息。status用 tinyint 存数字而不是直接存「待支付」这样的中文一是省空间二是改状态名不用改表结构只改后端枚举或常量类即可。3.2 用外键还是不用外键订购系统里的取舍数据库文档里如果画了外键关系你可能会纠结要不要在表结构里真的写 FOREIGN KEY。我的建议是逻辑外键而不是物理外键。也就是表结构里保留 user_id、order_id 这类字段也建索引但不声明 FOREIGN KEY 约束。原因有两个。第一InnoDB 的物理外键在插入、删除时会自动检查关联行会额外加锁高并发下单场景下容易成为性能瓶颈。第二物理外键让数据删除变得很麻烦——你想删一个测试用户它下过订单外键约束会阻止你删你必须先删订单。而真实业务里用户数据是需要保留的即便注销账号也只是把 user 表的 status 改成禁用谁会真的 DELETE所以常见做法是外键关系只体现在设计文档的 ER 图里建表语句里只保留索引数据一致性由 Service 层控制。这一点在答辩时也能作为设计亮点讲比「老师让加外键所以加了」听起来专业得多。3.3 订单状态机字段设计待支付、已下单、配送中、已完成怎么流转订单状态是订购系统里最容易出 bug 的地方因为它的流转是单向且有序的。常见状态定义是0 待支付 → 1 已下单待配送 → 2 配送中 → 3 已完成另外 4 已取消是一个从 0 或 1 可以跳转过去的兜底状态。0待支付用户刚下单但还没付钱。此时只锁定订单不真正扣库存或者扣减预占库存。1已下单待配送支付完成订单进入配送队列。2配送中配送员扫单出发系统生成配送记录。3已完成用户确认收货或配送员确认送达。4已取消用户取消或超时未支付系统自动取消。后端代码里不要直接用魔法数字散落各处我一般会用一个常量类或枚举统一管理public class OrderStatus { public static final int UNPAID 0; public static final int WAIT_DELIVERY 1; public static final int DELIVERING 2; public static final int FINISHED 3; public static final int CANCELLED 4; }代码说明定义成常量之后Service 层每次更新状态都调用setStatus(OrderStatus.DELIVERING)而不是setStatus(2)。好处是代码评审时一眼能看出状态流转是否符合预期比如禁止从 2配送中直接变回 1待配送这类非法流转应该在 Service 里显式判断并抛异常。状态流转是所有订单系统的核心业务逻辑宁可在这一块多写几个 if也不要图省事。配送记录表和订单表是 1 对多的关系一笔订单可能分三次配送每次配送生成一条 delivery_record。这样设计统计「今天要送多少单」时直接查 delivery_record 里 delivery_date 等于今天且 status 为待配送的记录即可不需要再去订单表里倒腾。4. 把系统跑起来的完整步骤从导入 SQL 到首次下单拿到源码包第一步不是打开 IDEA 就开始看代码而是先把环境对齐。这套系统因为是课设/毕设风格对运行环境比较敏感JDK 版本、MySQL 版本、Maven 仓库版本任何一个不对启动就会翻车。本节给出可复现的最小步骤按顺序执行多数项目都能在半小时内跑起来。4.1 环境准备JDK 版本与 IDEA 运行 JavaWeb 项目的基础配置先检查本机环境命令行执行java -version mvn -version mysql --version代码说明java -version 会输出 JDK 版本号mvn -version 会输出 Maven 版本mysql --version 是数据库客户端版本。这三条命令能立刻发现环境里的最大隐患——比如你装的是 JDK 17但项目要求 JDK 8。SpringBoot 2.x 项目在 JDK 17 下能启动但 Lombok 旧版本会报cannot access class之类的编译错误。如果你用的是 IDEA打开项目后还要确认三件事Project SDK 选择的是本机安装的 JDKMaven 配置里选了本地仓库而不是默认的 C 盘临时目录项目的编码是 UTF-8。IDEA 里运行 JavaWeb 项目不需要像旧时代那样手动配置 Tomcat 端口SpringBoot 内嵌了 Tomcat直接运行启动类的 main 方法即可。这一步对新手来说是最容易懵的——到处找 Tomcat 配置其实根本不用配。4.2 导入数据库脚本用 SQL 检查你的表结构项目包里通常带一个sql或db目录下的 .sql 脚本。命令行导入方式mysql -u root -p milk_ordering.sql代码说明-u root指定用户名-p提示输入密码是 shell 的重定向符把 sql 文件内容喂给 mysql 客户端执行。如果你的 .sql 文件里本身没有CREATE DATABASE语句需要先手动建库CREATE DATABASE IF NOT EXISTS milk_db DEFAULT CHARSET utf8mb4; USE milk_db; SHOW TABLES;参数说明utf8mb4 是必须的它在 MySQL 8 里能完整支持中文和特殊符号比 utf8 更保险。SHOW TABLES 之后如果看到 user、orders、order_item 这些表说明导入成功。接下来做一步关键验证——看看初始数据里有没有测试账号SELECT id, username, role FROM user; SELECT order_no, status FROM orders LIMIT 5;这一步能看出脚本里是否自带测试数据和演示订单。很多课设项目的数据库文档里会写明测试账号比如管理员 admin / admin123但你实际导入后要自己去查表确认不要轻信文档——文档落后于代码是常态以库里真实的记录为准。4.3 修改 application.yml四个必调参数SpringBoot 的配置文件后缀一般是 .properties 或 .yml现在新项目几乎都用 yml缩进层级清晰。打开src/main/resources/application.yml重点看这段server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/milk_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true这里有四个必调参数任何一个不对都连不上库参数常见值说明server.port8080端口被占用时改 8081/8082url 中的数据库名milk_db必须和 4.2 里建库的名字一致username / passwordroot / 你自己的密码本地 MySQL 的账号密码serverTimezoneAsia/Shanghai不配这个 MySQL 8 必报时区错误参数说明useSSLfalse是因为本地开发环境一般没配 SSL 证书不关掉会看到一串 SSL 警告characterEncodingutf8保证从数据库读写中文不乱码map-underscore-to-camel-case开启后数据库的create_time字段能自动映射到实体类的createTime驼峰属性省了一堆 Alias 注解。如果连接报Public Key Retrieval is not allowed在 url 后面再加allowPublicKeyRetrievaltrue这是 MySQL 8 和旧驱动之间最经典的报错之一。4.4 启动项目并用 Postman 验证登录与下单在 IDEA 里右键运行 MilkOrderApplication看到Started MilkOrderApplication字样后用浏览器访问http://localhost:8080能跳到登录页或者返回 JSON说明 Web 层已经起来了。接下来验证最重要的接口——登录和下单。用 Postman 发一个登录请求POST http://localhost:8080/api/user/login Content-Type: application/json { username: admin, password: admin123 }代码说明这是最常见的 POST JSON 接口。正常返回会带一个 token 字符串或者把用户信息放在 data 字段里。拿到登录凭证后下单接口带着它访问POST http://localhost:8080/api/order/create Authorization: Bearer 上面拿到的token Content-Type: application/json { milkId: 1, quantity: 2, addressId: 1 }代码说明token 这层鉴权是这个小系统的核心分界线。如果你调下单接口返回 401 或「未登录」说明后端有拦截器校验 token你需要先确保登录接口真的返回了 token并且 Authorization 头的格式和拦截器里写的一致。如果返回 500最直接的办法是去 IDEA 控制台看异常堆栈绝大多数下单失败原因会在日志里写得明明白白比如NullPointerException多半是 milkId 对应的商品没查到DataIntegrityViolationException多半是必填字段没传。用 Postman 验证的好处是它把前后端问题彻底隔离开了——前端页面打不开可能是 JS 报错但用 Postman 调接口也失败那就一定是后端的问题别在浏览器上浪费时间。5. 上线前必看订购系统最常见的 5 个翻车场景与排查路径这个项目跑起来容易跑到「看起来没问题」也容易但放到真实场景里用有几个坑是高频出现的。下面按实际踩坑频率排序每一条都给出现象、原因、解决三步前两条是环境问题后三条是代码问题你会发现很多所谓「玄学 bug」其实都是版本错配和事务误用。5.1 端口被占用内嵌 Tomcat 起不来现象启动日志里出现Port 8080 was already in use进程直接退出。原因本机某个程序占用了 8080 端口或者你之前启动过一次项目没关掉残留的 Java 进程还在。解决先查端口占用再决定是杀进程还是换端口。# Windows netstat -ano | findstr 8080 taskkill /PID 12345 /F # Linux/macOS lsof -i:8080 kill -9 12345代码说明netstat / lsof 能列出占用 8080 端口的进程 PID确认是自己的残留进程后强制结束。如果你不想杀进程直接改 application.yml 里的server.port更省事。这个坑没有技术含量但几乎每个新手都遇到过而且最容易和「项目本身坏了」混淆——先看这个再查别的。5.2 连不上 MySQLCommunications link failure 与 Access denied现象项目启动时抛出Communications link failure或Access denied for user rootlocalhost控制台红字一大片看着像项目崩了其实是数据库连接失败。原因前者基本是 url 里的 serverTimezone 没配或者 MySQL 端口不是默认的 3306后者是用户名或密码错误也可能是 MySQL 8 的 caching_sha2_password 加密插件和旧驱动不兼容。解决把 url 改成完整的连接串密码确认一遍。这里有个小技巧先用 Navicat/命令行手动连一下同一个库能连上再去排查代码。手动连都失败就是账号或服务本身的问题手动连成功、代码里失败就一定是连接字符串或驱动版本的问题不用怀疑数据库配置。MySQL 8 建议驱动用com.mysql.cj.jdbc.Driver这个类在 mysql-connector-j 驱动包里旧版com.mysql.jdbc.Driver在 MySQL 8 下已经废弃。5.3 下单成功但库存没扣事务为什么没生效现象接口返回下单成功订单也查得到但 milk_category 表里的 stock 数量纹丝不动。金额是对的明细也有就是库存没减。原因这是一个典型的「事务没生效」事故。常见有三种情况一是 Service 类内部方法自调用比如 OrderService 里的 createOrder 调用了本类的 deductStock事务注解被 Spring 的代理机制跳过二是异常被 try-catch 捕获后吞掉了事务看到没有异常抛出就执行了提交但实际上扣库存的 SQL 根本没执行三是 Transactional 加在了非 public 方法上Spring 的声明式事务默认只对 public 方法生效。解决事务方法不要同类自调用把它拆到另一个 Service 里注入进来事务方法里不要 try-catch 吞异常如果非要捕获至少加TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()手动标记回滚。核心代码模式是Service public class OrderService { public void createOrder(OrderCreateDTO dto) { orderMapper.insert(order); stockService.deductStock(dto.getMilkId(), dto.getQuantity()); } } Service public class StockService { Transactional(rollbackFor Exception.class) public void deductStock(Long milkId, Integer quantity) { stockMapper.deductStock(milkId, quantity); } }代码说明rollbackFor Exception.class一定要写因为 Spring 默认只对 RuntimeException 回滚如果里面抛的是受检异常比如 IOException不加这个参数就不会回滚。把 deductStock 拆出去之后createOrder 拿到 StockService 的代理对象调用事务注解才会真正生效。这里也是血泪经验最集中的地方——「单测看着没问题一压并发库存就超卖」十有八九就是事务边界画错了。5.4 日期返回成时间戳前后端时间显示不对现象数据库里 create_time 明明是2025-06-01 08:30:00但接口返回给前端的是一个长数字比如1717191400000前端页面上显示的日期完全不可读。原因Jackson 序列化 Java 的 Date 对象时默认输出为时间戳毫秒值SpringBoot 默认没有打开日期格式化。解决在 application.yml 里加一段配置即可spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8代码说明date-format 指定序列化格式time-zone 指定为东八区否则服务器部署在海外时时间会差 8 个小时。如果你用 MyBatis实体类里的日期字段建议用java.util.Date或LocalDateTime其中 LocalDateTime 是更现代的选择配合 MyBatis 3.5 不需要额外类型处理器。这一个配置解决后前端再看到时间戳就得回头查是不是前端自己又做了二次格式化。5.5 导入 SQL 报错MySQL 8 下的 ONLY_FULL_GROUP_BY 和过时语法现象Navicat 导入项目自带的 .sql 脚本时报错或者某个统计查询运行时报this is incompatible with sql_modeonly_full_group_by。原因项目同学在 MySQL 5.7 或更早版本上导出的脚本到了 MySQL 8 环境默认 sql_mode 更严格。比如 SELECT 列表里出现不在 GROUP BY 子句里的列、或者用了 MySQL 8 已经移除的语法都会直接报错。解决两个思路。一是临时放宽本次会话的 sql_mode适合急着看数据的情况SET SESSION sql_mode STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION;代码说明SET SESSION只对当前连接生效对线上环境没有持久影响。二是从根上改 SQL——把查询里那些依赖隐式分组的写法改成标准的GROUP BY或使用聚合函数。如果你只是要把这个系统跑起来做演示建议直接用 MySQL 5.7 的 Docker 镜像跑省去所有兼容性折腾如果你是接一个真实项目就得老老实实把脚本里的问题 SQL 一条条改掉这个没有捷径。6. 让订购系统走出「课设」周期配送、幂等下单与前后端分离的三个增强点如果你不满足于「跑通、答辩、毕业」这个终点想把订购系统做成能接真实订单的产品下面三个增强方向是最值得优先投入的。6.1 把「一次性订单」改成「周期订阅」现在的表结构里订单和配送是绑定在一次交易上的。真实鲜牛奶业务大多是按月订阅用户订的是「每周一、三、五送两瓶巴氏奶」。改造思路是给订单表加一个subscribe_type字段0 单次 / 1 周期再配合定时任务在每天凌晨生成当天的配送单。用 SpringBoot 自带的 Scheduled 就能实现Scheduled(cron 0 30 5 * * ?) public void generateTodayDelivery() { ListOrder subscribeOrders orderMapper.findSubscribeOrders(); for (Order order : subscribeOrders) { deliveryMapper.insertTodayDelivery(order); } }代码说明cron 表达式0 30 5 * * ?表示每天早上 5 点 30 分执行。这个改造做完系统才真正配得上「订购」而不是「下单」。6.2 防止重复下单与超卖幂等是底线生产环境里用户双击提交按钮、前端重试都会导致同一笔订单在数据库里出现两条。常见做法是在订单表加唯一约束uk_user_milkuser_id milk_id create_date数据库帮你挡住重复同时扣库存的 SQL 必须写成条件更新UPDATE milk_category SET stock stock - #{quantity} WHERE id #{milkId} AND stock #{quantity}代码说明注意AND stock #{quantity}这个条件它保证库存不足时更新不到行再结合受影响行数为 0 则回滚就能避免超卖。这一行条件比你在代码里加十层 if 都管用。6.3 把页面拆出去Vue 打包放进 SpringBoot课设通常用 Thymeleaf 模板或 jQuery 直接操作 DOM但真实项目里前后端分离是标配。一个很顺滑的过渡方案是前端用 Vue 开发npm run build之后把 dist 目录里的静态文件复制到src/main/resources/static下SpringBoot 会自动托管它们。接口路径统一加/api前缀然后在后端配置一个拦截器放行静态资源、拦截 /api 下的所有请求用 JWT 做鉴权。我自己接过一个类似的订购项目最深的教训是不要相信「测试数据删了重建」这句话。订单表一旦有真实流水删库重来会让你丢光客户信任。我现在的习惯是核心表都加一个deleted逻辑删除字段真删只发生在数据确实录错的 24 小时内。这个项目如果只是拿来交作业跑通前面五章就足够了如果想拿它去接真实业务先把第 3 章的状态机和第 6 章的幂等逻辑做好再上线也不迟。希望帮到你。本文还有配套的精品资源点击获取
返回列表