SpringBoot3 + Vue 订单库存物流项目完整案例

目录

[1. 项目概述](#1. 项目概述)

[1.1 业务需求分析](#1.1 业务需求分析)

[1.2 核心业务流程](#1.2 核心业务流程)

[1.3 技术选型](#1.3 技术选型)

[1.3.1 后端技术栈](#1.3.1 后端技术栈)

[1.3.2 前端技术栈](#1.3.2 前端技术栈)

[1.3.3 开发环境与工具](#1.3.3 开发环境与工具)

[2. 数据库设计](#2. 数据库设计)

[2.1 核心 ER 实体关系图](#2.1 核心 ER 实体关系图)

[2.2 数据库表设计](#2.2 数据库表设计)

[2.2.1 商品表(products)](#2.2.1 商品表(products))

[2.2.2 库存表(inventory)](#2.2.2 库存表(inventory))

[2.2.3 订单表(orders)](#2.2.3 订单表(orders))

[2.2.4 订单明细表(order_detail)](#2.2.4 订单明细表(order_detail))

[2.2.5 物流订单表(logistics_order)](#2.2.5 物流订单表(logistics_order))

[2.2.6 物流轨迹表(logistics_trace)](#2.2.6 物流轨迹表(logistics_trace))

[2.3 核心业务关联关系](#2.3 核心业务关联关系)

[2.4 初始化 SQL 脚本](#2.4 初始化 SQL 脚本)

[3. 开发环境搭建](#3. 开发环境搭建)

[3.1 后端 Spring Boot 工程搭建](#3.1 后端 Spring Boot 工程搭建)

[3.1.1 工程初始化](#3.1.1 工程初始化)

[3.1.2 配置文件调整](#3.1.2 配置文件调整)

[3.1.3 引入必要依赖](#3.1.3 引入必要依赖)

[3.1.4 工程包结构规划](#3.1.4 工程包结构规划)

[3.2 前端 Vue 工程搭建](#3.2 前端 Vue 工程搭建)

[3.2.1 工程初始化](#3.2.1 工程初始化)

[3.2.2 项目结构规划](#3.2.2 项目结构规划)

[3.2.3 跨域配置](#3.2.3 跨域配置)

[3.3 前后端联调验证](#3.3 前后端联调验证)

[4. 后端核心业务开发](#4. 后端核心业务开发)

[4.1 公共层基础开发](#4.1 公共层基础开发)

[4.1.1 统一返回结果封装](#4.1.1 统一返回结果封装)

[4.1.2 全局异常处理](#4.1.2 全局异常处理)

[4.1.3 工具类开发](#4.1.3 工具类开发)

[4.2 商品模块开发](#4.2 商品模块开发)

[4.2.1 实体类(Entity)](#4.2.1 实体类(Entity))

[4.2.2 Mapper 层](#4.2.2 Mapper 层)

[4.2.3 Service 层](#4.2.3 Service 层)

[4.2.4 Controller 层](#4.2.4 Controller 层)

[4.3 库存模块开发](#4.3 库存模块开发)

[4.3.1 实体类(Entity)](#4.3.1 实体类(Entity))

[4.3.2 Mapper 层](#4.3.2 Mapper 层)

[4.3.3 Service 层](#4.3.3 Service 层)

[4.3.4 Controller 层](#4.3.4 Controller 层)

[4.4 订单模块开发](#4.4 订单模块开发)

[4.4.1 实体类(Entity)](#4.4.1 实体类(Entity))

[4.4.2 Mapper 层](#4.4.2 Mapper 层)

[4.4.3 Service 层](#4.4.3 Service 层)

[4.4.4 Controller 层](#4.4.4 Controller 层)

[4.5 物流模块开发](#4.5 物流模块开发)

[4.5.1 实体类(Entity)](#4.5.1 实体类(Entity))

[4.5.2 Mapper 层](#4.5.2 Mapper 层)

[4.5.3 Service 层](#4.5.3 Service 层)

[4.5.4 Controller 层](#4.5.4 Controller 层)

[5. 前端核心业务开发](#5. 前端核心业务开发)

[5.1 公共层封装](#5.1 公共层封装)

[5.1.1 后端 API 接口统一封装](#5.1.1 后端 API 接口统一封装)

[5.1.2 路由配置](#5.1.2 路由配置)

[5.2 订单列表页开发](#5.2 订单列表页开发)

[5.2.1 页面结构](#5.2.1 页面结构)

[5.2.2 交互逻辑](#5.2.2 交互逻辑)

[5.3 物流跟踪页开发](#5.3 物流跟踪页开发)

[5.3.1 页面结构](#5.3.1 页面结构)

[5.3.2 交互逻辑](#5.3.2 交互逻辑)

[5.4 前后端联调验证](#5.4 前后端联调验证)

[6. 测试用例编写](#6. 测试用例编写)

[6.1 测试环境配置](#6.1 测试环境配置)

[6.2 Service 层单元测试](#6.2 Service 层单元测试)

[6.2.1 库存扣减测试(InventoryService)](#6.2.1 库存扣减测试(InventoryService))

[6.2.2 订单流程测试(OrderService)](#6.2.2 订单流程测试(OrderService))

[6.2.3 物流状态流转测试(LogisticsService)](#6.2.3 物流状态流转测试(LogisticsService))

[6.3 Controller 层集成测试](#6.3 Controller 层集成测试)

[6.3.1 订单接口测试(OrderController)](#6.3.1 订单接口测试(OrderController))

[6.3.2 物流接口测试(LogisticsController)](#6.3.2 物流接口测试(LogisticsController))

[6.3.3 库存接口测试(InventoryController)](#6.3.3 库存接口测试(InventoryController))

[6.4 测试执行与报告生成](#6.4 测试执行与报告生成)

[7. 总结与扩展优化](#7. 总结与扩展优化)

[7.1 项目总结](#7.1 项目总结)

[7.2 扩展优化方向](#7.2 扩展优化方向)

[7.2.1 高并发场景优化](#7.2.1 高并发场景优化)

[7.2.2 物流轨迹实时更新优化](#7.2.2 物流轨迹实时更新优化)

[7.2.3 状态流转优化](#7.2.3 状态流转优化)

[7.2.4 基础设施优化](#7.2.4 基础设施优化)

[7.2.5 业务场景优化](#7.2.5 业务场景优化)


从设计文档、工程搭建到业务实现、测试验证的全链路开发指南

1. 项目概述

本文将基于 Spring Boot 3 与 Vue 3 技术栈,开发一套包含订单下单、商品库存扣减、物流状态变更的完整业务案例。该案例聚焦核心业务流,不引入复杂权限体系,兼顾功能可扩展性与高并发场景下的业务稳定性,适合作为全栈开发技术验证、中小型业务系统基础脚手架,或在校学生课程设计 / 毕业设计的参考工程。

1.1 业务需求分析

本案例核心业务围绕 "用户下单→库存扣减→物流更新→订单状态同步" 的闭环流程展开,不涉及权限管理、支付对接等附加功能,核心需求覆盖订单、库存、物流三个模块的基础业务逻辑:

订单模块:用户可提交包含多商品的订单,提交后系统将自动生成订单记录,同时支持查询订单列表、查看订单详情;在库存不足、物流异常等场景下,系统需支持手动或自动取消订单,取消后同步恢复对应库存数量。

库存模块:需严格保证扣减操作的准确性,避免高并发场景下出现超卖、库存为负等异常情况;库存扣减需与订单创建、物流发货流程实现协同一致性,即订单创建时预扣库存、物流发货时确认扣减,若订单取消则自动恢复预扣库存。

物流模块:为已支付 / 已确认发货的订单分配物流单号,支持录入或同步物流运输轨迹信息,物流状态需覆盖 "待发货→发货中→运输中→已签收" 的完整生命周期;同时提供物流单号与订单号的双向关联查询,支持通过订单号获取最新物流状态。

状态联动约束:订单、库存、物流的状态变更需遵循强制业务联动规则,且所有变更操作需保留可溯源的操作记录。

1.2 核心业务流程

本案例的核心业务流程以 "用户下单" 为起点,以 "订单完成 / 关闭" 为终点,串联订单、库存、物流三大模块的核心操作,完整流程的关键节点如下:

用户下单:用户在前端确认商品、数量、收货地址等信息后,提交订单请求;后端接收请求后,首先校验商品的基础库存是否充足,若库存不足则直接返回 "商品库存不足" 的异常提示。

创建订单:若前置库存校验通过,后端将生成状态为 "待发货" 的订单初始记录;此时系统仅锁定对应商品的预扣库存,不会直接扣减商品的实际库存,确保用户有足够的时间完成后续确认操作。

库存扣减:订单确认生效后,系统将执行实际库存扣减操作;为保证高并发场景下的扣减准确性,扣减过程需采用 "Redis 预扣 + 数据库最终校验" 的方案,同时通过数据库乐观锁避免同一库存被重复扣减。

物流发起:库存扣减完成后,系统自动将订单状态流转为 "待发货",并触发物流业务流程;此时可通过人工录入或调用第三方物流平台接口的方式,为该订单分配唯一的物流单号。

物流状态更新:在货物运输的全流程中,物流节点的操作人员(如揽件员、中转站分拣员、派件员)会通过移动端或后台系统更新物流节点状态;所有状态变更将同步到订单系统,保持订单与物流状态的实时统一。

订单完成:当物流状态更新为 "已签收" 后,系统将自动同步订单状态为 "已完成",并记录订单完成时间;若在物流运输过程中发生异常(如包裹丢失、用户拒收),员工可录入异常物流状态,系统将根据状态类型触发对应的逆向流程(如库存回流、订单关闭)。

1.3 技术选型

本案例采用业界主流的前后端分离架构,技术选型以 "稳定、通用、易落地" 为核心原则,所有技术栈均是企业级项目中广泛使用的成熟方案。

1.3.1 后端技术栈

后端技术栈以 Spring Boot 3 为核心,覆盖 Web 服务、数据访问、缓存、事务管理等全链路能力,具体选型及说明如下:

核心框架:Spring Boot 3.2.x 作为项目核心框架,提供自动化配置、嵌入式 Web 容器、依赖注入等核心能力,简化企业级应用的初始搭建与后续维护;该版本要求 JDK 版本不低于 17,以支撑其虚拟线程、核心 API 优化等新特性的落地。

数据库:选择 MySQL 8.0 作为关系型数据库,存储订单、商品、库存、物流等所有业务数据;MySQL 8.0 的新特性(如增强的事务隔离级别、JSON 类型字段支持)可更好支撑物流轨迹、订单扩展属性等非结构化数据的存储需求。

ORM 框架 :选用 MyBatis-Plus 3.5.3.1 作为数据访问层框架,它在 MyBatis 的基础上封装了单表 CRUD、分页、条件构造器等通用操作,同时兼容原生 MyBatis 的 SQL 编写能力,既能减少重复代码开发量,又能灵活支撑复杂关联查询的 SQL 优化需求(61)

缓存 :采用 Redis 7.2 作为缓存中间件,主要承担两个核心职责:一是在高并发下单场景中,通过原子化操作预扣库存,减少对数据库的直接压力;二是缓存热点数据(如商品基础信息、用户常用地址),提升系统整体响应速度(37)

工具类:项目中引入 Lombok 简化实体类的 getter/setter、构造方法等重复代码的编写,同时引入 Spring Boot Starter Validation 作为参数校验工具,对所有前端传入的请求参数进行合法性校验,避免非法参数进入业务流程。

接口文档 :整合 Knife4j-OpenAPI 3.0 框架,自动生成符合 RESTful 规范的接口文档;文档支持在线调试、参数 Mock、响应示例查看等功能,可有效降低前后端联调、后期接口维护的沟通成本(100)

1.3.2 前端技术栈

前端技术栈以 Vue 3 为核心,搭配现代化的构建工具、UI 组件库,开发体验与页面性能更适配企业级业务需求,具体选型及说明如下:

核心框架 :Vue 3.4.x 作为核心视图框架,其组合式 API(Composition API)可实现更灵活的业务逻辑复用,在大型业务页面开发时能更精准地控制组件更新粒度,提升页面渲染性能(1)

构建工具 :选用 Vite 4.x 作为前端构建工具,相比传统 Webpack 构建工具,Vite 基于 ESBuild 预构建依赖、利用浏览器原生 ES 模块发起请求,在开发环境下的冷启动、热更新效率有量级级提升,可显著缩短本地开发调试的等待时间(85)

UI 组件库 :采用 Element Plus 3.x 作为基础 UI 组件库,它提供了表格、分页、表单、对话框、面包屑等企业级业务开发常用的高质量组件,且所有组件都遵循 WCAG 无障碍访问规范,开箱即用的特性可大幅降低页面开发量(63)

状态管理:选用 Pinia 2.x 作为 Vue 应用的状态管理工具,它是 Vue 官方推荐的状态管理方案,相比传统 Vuex,Pinia 提供了更简洁的 API、更完善的 TypeScript 类型推断支持,且代码分割逻辑更轻量化,便于在订单、物流等跨模块业务场景下共享状态。

路由管理 :采用 Vue Router 4.x 作为前端路由管理器,实现订单列表、物流跟踪、详情页等业务页面的无刷新切换;配合 Vue 3 的 keep-alive 组件,可实现业务页面的缓存功能,提升用户交互体验(85)

HTTP 客户端 :选用 Axios 1.6.x 作为 HTTP 请求工具,封装前端与后端的所有 API 交互;通过请求 / 响应拦截器统一预处理请求参数、格式化响应数据、处理全局异常,还可在请求端启用代理配置,解决前后端联调时的跨域问题(85)

地图组件:为了更直观地展示物流运输轨迹,项目中引入了高德地图 JSAPI 的 Vue 封装组件,可在物流跟踪页面上以可视化路线图形式渲染运输轨迹、标记当前包裹位置。

1.3.3 开发环境与工具

为保证项目在开发、联调、部署阶段的环境一致性,规避因工具版本差异导致的兼容性问题,团队对开发工具的版本选型进行了明确约束:

JDK 版本:选择与 Spring Boot 3.2.x 兼容的 JDK 17,这是 Spring Boot 3.x 系列的最低要求版本,无论是 Oracle JDK 还是 OpenJDK,只要版本号不低于 17,均可支撑项目开发;

Node.js 版本:选择 LTS 18.16.0 及以上版本,这是 Vite 4.x 的要求版本,可保证前端项目的构建、热更新等功能正常运行;

开发 IDE:后端推荐使用 IntelliJ IDEA Ultimate 版,该工具对 Spring Boot、MyBatis-Plus 提供了开箱即用的支持,如配置文件自动提示、Bean 依赖关系查看、Mapper 方法与 SQL 的跳转关联;前端推荐使用 VS Code,配合 Vue Official、Vite、Element UI Snippets 等官方插件,可大幅提升编码效率、降低编码错误率(83)

其他工具:项目中采用 Maven 3.8.x 作为后端依赖管理工具,使用 Git 进行版本控制,同时采用了更轻量化的 Postman 或 Apifox 工具进行后端接口调试,确保接口功能符合业务需求(85)

2. 数据库设计

本案例采用 MySQL 8.0 作为数据库,严格遵循第三范式(3NF)设计核心业务表,通过合理的字段冗余、索引设计,在满足数据一致性的前提下,优先保障订单查询、物流跟踪等高频业务场景的性能表现。

2.1 核心 ER 实体关系图

本案例的核心业务实体包含商品、订单、订单详情、库存、物流订单、物流轨迹,实体间的关联关系为:一个订单可包含多个订单详情(行项),每个订单详情对应一件商品;一件商品对应一条库存记录,存储当前仓库的实际库存数量;一个订单关联唯一一条物流订单记录,一条物流订单对应多个物流轨迹节点,从而完整覆盖 "商品 - 订单 - 库存 - 物流" 的全链路业务关联逻辑(12)

2.2 数据库表设计

根据上述实体关系,设计出符合业务约束的核心业务表结构。为保证所有业务表的字段一致性、后期可维护性,设计时对部分字段进行了统一约束:所有业务表均使用id作为主键,所有表的create_time字段默认取当前系统时间,update_time字段设置为 "在数据更新时自动更新为当前时间";同时,为了实现数据的柔性删除、避免物理删除导致的历史数据丢失,所有表均增加了is_deleted逻辑删除字段。

2.2.1 商品表(products)

商品表存储所有上架销售的商品基础信息,是订单、库存、物流业务的核心关联基础表,唯一约束为 "商品编号唯一",其表结构设计如下:

|---------------|---------------|-------------------------|--------|
| 字段名 | 类型 | 含义 | 约束 |
| id | BIGINT | 商品 ID | 主键、自增 |
| spu_name | VARCHAR(100) | 商品 SPU 名称 | 非空 |
| spu_value | VARCHAR(100) | 商品规格(如尺码、颜色) | 非空 |
| spu_price | DECIMAL(10,2) | 商品售价 | 非空 |
| product_sales | INT | 商品销量 | 默认 0 |
| state | TINYINT | 商品上下架状态(0 - 上架,1 - 下架) | 默认 0 |
| create_time | DATETIME | 创建时间 | 默认当前时间 |
| update_time | DATETIME | 更新时间 | 默认当前时间 |
| is_deleted | TINYINT | 逻辑删除标识(0 - 未删除,1 - 已删除) | 默认 0 |

其中,spu_name、spu_value、spu_price字段的业务定义与存储约束,和此前的电商类项目设计保持一致,保证了业务逻辑的连贯性;同时,表中针对spu_name、state字段创建了联合索引,优化商品列表页的查询、过滤性能(95)

2.2.2 库存表(inventory)

库存表存储商品的实际库存、预扣库存数据,是保证库存精准扣减、避免超卖问题的核心业务表,其表结构设计如下:

|--------------|----------|-------------------------|---------|
| 字段名 | 类型 | 含义 | 约束 |
| id | BIGINT | 库存记录 ID | 主键、自增 |
| product_id | BIGINT | 关联商品 ID | 非空、唯一 |
| warehouse_id | BIGINT | 仓库 ID | 非空,默认 1 |
| stock_num | INT | 实际库存数量 | 非空,默认 0 |
| locked_stock | INT | 预扣 / 锁定库存数量 | 非空,默认 0 |
| version | INT | 乐观锁版本号 | 非空,默认 0 |
| create_time | DATETIME | 创建时间 | 默认当前时间 |
| update_time | DATETIME | 更新时间 | 默认当前时间 |
| is_deleted | TINYINT | 逻辑删除标识(0 - 未删除,1 - 已删除) | 默认 0 |

该表的核心设计细节如下:

为了支持多仓库业务扩展,表中增加了warehouse_id字段,作为后续扩展多仓库库存隔离的基础标识;

通过stock_num(实际库存)与locked_stock(锁定库存)两个字段,将 "可售库存" 拆分为 "待扣减库存" 与 "已预扣库存" 两部分,只有支付或发货确认后,锁定库存才会被实际扣减,有效避免超卖、库存不一致的问题(32)

引入version字段作为乐观锁标识,每次执行库存扣减前,会先校验数据库中的版本号与业务逻辑中取出的版本号是否一致,只有一致才会执行扣减操作,避免同一库存被多个请求重复扣减(36)

为了保证查询性能,表中针对product_id、warehouse_id字段创建了联合唯一索引,提升商品库存的关联查询、更新速度(32)

2.2.3 订单表(orders)

订单表存储用户提交订单的核心基础信息,是整个订单库存物流业务的核心主表,其表结构设计如下:

|------------------|---------------|-------------------------|--------|
| 字段名 | 类型 | 含义 | 约束 |
| id | BIGINT | 订单 ID | 主键、自增 |
| order_no | VARCHAR(32) | 订单编号 | 非空、唯一 |
| user_id | BIGINT | 下单用户 ID | 非空 |
| total_amount | DECIMAL(10,2) | 订单总金额 | 非空 |
| status | VARCHAR(20) | 订单状态 | 非空 |
| payment_method | VARCHAR(50) | 支付方式 | 可选 |
| delivery_address | TEXT | 收货地址 | 非空 |
| create_time | DATETIME | 创建时间 | 默认当前时间 |
| update_time | DATETIME | 更新时间 | 默认当前时间 |
| is_deleted | TINYINT | 逻辑删除标识(0 - 未删除,1 - 已删除) | 默认 0 |

其中,order_no为对外业务标识,采用自定义规则生成(如时间戳 + 用户 ID 后缀),保证全局唯一,避免使用可遍历的数字主键,提升订单安全性;status字段的业务定义,与此前设计的订单状态机流转规则保持一致,其取值范围包括CREATED(已创建)、PAID(已支付)、SHIPPED(已发货)、COMPLETED(已完成)、CANCELLED(已取消),是订单、物流状态联动的核心依据(10)

2.2.4 订单明细表(order_detail)

订单明细表存储用户提交订单的行项商品信息,是 "订单 - 商品" 关联的中间表,一个订单可包含多个订单明细,其表结构设计如下:

|-------------|---------------|-------------------------|--------|
| 字段名 | 类型 | 含义 | 约束 |
| id | BIGINT | 订单明细 ID | 主键、自增 |
| order_id | BIGINT | 关联订单 ID | 非空 |
| product_id | BIGINT | 关联商品 ID | 非空 |
| quantity | INT | 购买商品数量 | 非空 |
| price | DECIMAL(10,2) | 商品下单时单价 | 非空 |
| total_price | DECIMAL(10,2) | 该行项商品总金额 | 非空 |
| create_time | DATETIME | 创建时间 | 默认当前时间 |
| update_time | DATETIME | 更新时间 | 默认当前时间 |
| is_deleted | TINYINT | 逻辑删除标识(0 - 未删除,1 - 已删除) | 默认 0 |

该表中,order_id与product_id的联合唯一约束,可避免同一订单重复添加同一个商品;同时,表中记录的price(商品下单时单价)是商品的快照数据,不可复用商品表中的spu_price,保证后续商品基础价格被调整时,订单的历史行项金额不会被影响,保障订单数据的可溯源性。

2.2.5 物流订单表(logistics_order)

物流订单表存储订单对应的物流单核心信息,是连接订单业务与物流运输业务的主表,一个订单对应唯一一条物流订单记录,其表结构设计如下:

|-------------------------|-------------|-------------------------|--------|
| 字段名 | 类型 | 含义 | 约束 |
| id | BIGINT | 物流单 ID | 主键、自增 |
| order_id | BIGINT | 关联订单 ID | 非空、唯一 |
| tracking_no | VARCHAR(50) | 物流单号 | 非空、唯一 |
| carrier | VARCHAR(20) | 承运商 | 可选 |
| status | VARCHAR(20) | 物流单当前状态 | 非空 |
| estimated_delivery_time | DATETIME | 预计送达时间 | 可选 |
| created_at | DATETIME | 创建时间 | 默认当前时间 |
| updated_at | DATETIME | 更新时间 | 默认当前时间 |
| is_deleted | TINYINT | 逻辑删除标识(0 - 未删除,1 - 已删除) | 默认 0 |

其中,tracking_no为物流商提供的唯一单号,status字段的取值范围包括WAITING_PICKUP(待揽件)、PICKED_UP(已揽件)、IN_TRANSIT(运输中)、DELIVERED(已送达)、SIGNED(已签收),是物流状态同步给订单的核心依据(99)

2.2.6 物流轨迹表(logistics_trace)

物流轨迹表存储物流订单的全流程运输节点信息,是物流跟踪功能的核心支撑表,一条物流订单对应多个物流轨迹节点,其表结构设计如下:

|--------------------|--------------|-------------------------|--------|
| 字段名 | 类型 | 含义 | 约束 |
| id | BIGINT | 轨迹 ID | 主键、自增 |
| logistics_order_id | BIGINT | 关联物流单 ID | 非空 |
| trace_time | DATETIME | 节点发生时间 | 非空 |
| status | VARCHAR(20) | 节点物流状态 | 非空 |
| description | VARCHAR(255) | 物流节点详情描述 | 可选 |
| location | VARCHAR(255) | 节点经纬度位置 | 可选 |
| created_at | DATETIME | 创建时间 | 默认当前时间 |
| updated_at | DATETIME | 更新时间 | 默认当前时间 |
| is_deleted | TINYINT | 逻辑删除标识(0 - 未删除,1 - 已删除) | 默认 0 |

该表中,location字段存储物流节点的经纬度信息,用于在前端地图上渲染运输轨迹;description字段存储物流节点的详细描述(如 "【广州市】广州白云区公司收件员已揽件"),对用户展示物流进展的详细说明;同时,表中针对logistics_order_id字段创建了二级索引,提升物流轨迹的多节点查询性能(26)

2.3 核心业务关联关系

为了保证订单、库存、物流数据的一致性,项目通过外键约束与业务逻辑的组合,建立了三张核心业务表之间的强关联关系,具体关联规则为:

订单明细表的order_id字段关联订单表的id主键,product_id字段关联商品表的id主键,确保订单行项的商品、用户收货地址等基础数据不会被物理删除;

库存表的product_id字段与商品表的id主键建立唯一关联,确保每一件上架商品都有独立的库存记录;

物流订单表的order_id字段关联订单表的id主键,且两者的关联关系为一对一,确保一个订单只能对应一个物流单;

物流轨迹表的logistics_order_id字段关联物流订单表的id主键,确保所有物流轨迹节点都能溯源到对应的订单。

在后续的高并发优化中,为了降低数据库的压力,可将部分外键约束(如物流轨迹表与物流订单表的外键)从数据库层面调整到业务逻辑层,由业务代码在更新数据时保证一致性。

2.4 初始化 SQL 脚本

本案例的所有数据库表初始化 SQL 脚本,均严格遵循 MySQL 8.0 的语法规范,核心业务表的 SQL 建表示意脚本如下:

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| -- 创建数据库(如果不存在) CREATE DATABASE IF NOT EXISTS `order_demo` DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; -- 使用数据库 USE `order_demo`; -- 商品表建表语句 CREATE TABLE IF NOT EXISTS `products` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '商品ID', `spu_name` VARCHAR(100) NOT NULL COMMENT '商品SPU名称', `spu_value` VARCHAR(100) NOT NULL COMMENT '商品规格', `spu_price` DECIMAL(10,2) NOT NULL COMMENT '商品售价', `product_sales` INT NOT NULL DEFAULT 0 COMMENT '销量', `state` TINYINT NOT NULL DEFAULT 0 COMMENT '上下架状态', `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除标识', PRIMARY KEY (`id`), INDEX `idx_spu_name` (`spu_name`), INDEX `idx_state` (`state`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表'; -- 库存表建表语句 CREATE TABLE IF NOT EXISTS `inventory` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '库存记录ID', `product_id` BIGINT NOT NULL COMMENT '商品ID', `warehouse_id` BIGINT NOT NULL DEFAULT 1 COMMENT '仓库ID', `stock_num` INT NOT NULL DEFAULT 0 COMMENT '实际库存数量', `locked_stock` INT NOT NULL DEFAULT 0 COMMENT '锁定库存数量', `version` INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除标识', PRIMARY KEY (`id`), UNIQUE INDEX `idx_product_warehouse` (`product_id`, `warehouse_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存表'; -- 订单表建表语句 CREATE TABLE IF NOT EXISTS `orders` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '订单ID', `order_no` VARCHAR(32) NOT NULL COMMENT '订单编号', `user_id` BIGINT NOT NULL COMMENT '用户ID', `total_amount` DECIMAL(10,2) NOT NULL COMMENT '订单总金额', `status` VARCHAR(20) NOT NULL COMMENT '订单状态', `payment_method` VARCHAR(50) DEFAULT NULL COMMENT '支付方式', `delivery_address` TEXT NOT NULL COMMENT '收货地址', `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除标识', PRIMARY KEY (`id`), UNIQUE INDEX `idx_order_no` (`order_no`), INDEX `idx_user_id` (`user_id`), INDEX `idx_create_time` (`create_time`), INDEX `idx_status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表'; -- 订单明细表建表语句 CREATE TABLE IF NOT EXISTS `order_detail` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '订单明细ID', `order_id` BIGINT NOT NULL COMMENT '订单ID', `product_id` BIGINT NOT NULL COMMENT '商品ID', `quantity` INT NOT NULL COMMENT '购买数量', `price` DECIMAL(10,2) NOT NULL COMMENT '商品下单单价', `total_price` DECIMAL(10,2) NOT NULL COMMENT '该行项总金额', `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除标识', PRIMARY KEY (`id`), INDEX `idx_order_id` (`order_id`), INDEX `idx_product_id` (`product_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单明细表'; -- 物流订单表建表语句 CREATE TABLE IF NOT EXISTS `logistics_order` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '物流单ID', `order_id` BIGINT NOT NULL COMMENT '订单ID', `tracking_no` VARCHAR(50) NOT NULL COMMENT '物流单号', `carrier` VARCHAR(20) DEFAULT NULL COMMENT '承运商', `status` VARCHAR(20) NOT NULL COMMENT '物流单状态', `estimated_delivery_time` DATETIME DEFAULT NULL COMMENT '预计送达时间', `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除标识', PRIMARY KEY (`id`), UNIQUE INDEX `idx_order_id` (`order_id`), UNIQUE INDEX `idx_tracking_no` (`tracking_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='物流订单表'; -- 物流轨迹表建表语句 CREATE TABLE IF NOT EXISTS `logistics_trace` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '轨迹ID', `logistics_order_id` BIGINT NOT NULL COMMENT '物流单ID', `trace_time` DATETIME NOT NULL COMMENT '节点时间', `status` VARCHAR(20) NOT NULL COMMENT '节点物流状态', `description` VARCHAR(255) DEFAULT NULL COMMENT '节点详情描述', `location` VARCHAR(255) DEFAULT NULL COMMENT '节点位置', `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `is_deleted` TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除标识', PRIMARY KEY (`id`), INDEX `idx_logistics_order_id` (`logistics_order_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='物流轨迹表'; |

实际开发时,可将上述脚本保存为database.sql文件,在 MySQL 客户端或 Navicat、DBeaver 等可视化工具中执行,完成初始化数据库与表结构的准备工作。

3. 开发环境搭建

本节将详细介绍从零基础搭建前后端分离项目的步骤,包含后端 Maven 工程创建、前端 Vue 工程初始化,以及工程配置、依赖整合的完整过程。

3.1 后端 Spring Boot 工程搭建

后端采用技术栈组合为 Spring Boot 3.2.x + MyBatis-Plus + MySQL 8.0 + Redis,工程构建工具为 Maven,可通过 Spring Initializr 或 IDE 可视化两种方式快速搭建基础工程。

3.1.1 工程初始化

本案例提供两种搭建后端基础工程的方式,开发者可根据自身习惯选择其中一种。

方式一:通过 Spring Initializr 创建

这是 Spring 官方提供的项目初始化工具,可通过浏览器访问 Spring Initializr 页面,快速生成基础工程骨架,具体操作步骤为:

选择项目构建工具为Maven,开发语言为Java,Spring Boot 版本选择3.2.x(如 3.2.5);

填写项目元信息:Group(如com.example)、Artifact(如order-demo)、Name、Description,包名、Java 版本选择17;

选择项目依赖,核心依赖包括Spring Web(提供 Web 服务能力)、MySQL Driver(提供 MySQL 数据库连接能力)、Lombok(简化实体类代码编写)、MyBatis Framework(数据库 ORM 框架)、Spring Data Redis(提供 Redis 缓存操作能力)、Validation(提供参数校验能力);

点击 "Generate" 按钮,自动下载生成的项目压缩包,解压后导入到开发 IDE 中(如 IntelliJ IDEA)。

方式二:通过 IntelliJ IDEA 可视化创建

如果使用 IntelliJ IDEA 作为开发 IDE,可直接通过 IDE 集成的 Spring Initializr 创建工程,具体操作步骤为:

打开 IDE 后,点击File → New → Project,选择Spring Initializr选项,设置项目元信息(Group、Artifact、Java 版本等);

选择 Spring Boot 版本为3.2.x,在依赖列表中勾选上述核心依赖;

确认项目名称、存储磁盘路径后,点击 "Finish" 按钮,完成项目初始化;IDE 将自动下载配置的依赖,等待下载完成即可进行后续开发。

3.1.2 配置文件调整

工程初始化完成后,需在application.properties(或application.yml)配置文件中添加数据库、Redis、MyBatis-Plus、项目端口等核心配置,确保项目能正常连接依赖资源、启动 Web 服务。本案例采用application.yml格式作为项目配置文件,核心配置内容示意如下:

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| # 应用服务配置 server: port: 9090 # 后端服务端口号 servlet: context-path: /api # 项目统一请求前缀 # 数据源配置 spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/order_demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true username: root # 数据库用户名 password: 123456 # 数据库密码 druid: initial-size: 5 max-active: 20 min-idle: 5 max-wait: 60000 # Redis配置 redis: host: localhost # Redis服务器地址 port: 6379 # Redis端口号 password: # Redis密码,默认为空 database: 0 # 选择Redis库 timeout: 3000ms lettuce: pool: max-active: 8 max-idle: 8 min-idle: 0 # MyBatis-Plus配置 mybatis-plus: mapper-locations: classpath:mapper/*.xml # 加载Mapper XML文件 type-aliases-package: com.example.orderdemo.entity # 配置实体类包别名 configuration: map-underscore-to-camel-case: true # 开启驼峰命名与下划线转换 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: id-type: auto # 数据库主键自增 logic-delete-field: is_deleted # 逻辑删除字段 logic-delete-value: 1 # 逻辑删除已删除值 logic-not-delete-value: 0 # 逻辑删除未删除值 # 日志配置 logging: level: com.example.orderdemo.mapper: debug # 开启SQL日志打印 |

配置完成后,需在工程的src/main/java/com/example/orderdemo目录下创建启动类OrderDemoApplication.java,添加@SpringBootApplication注解,标识这是一个 Spring Boot 启动类,代码示例如下:

|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| package com.example.orderdemo; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @MapperScan("com.example.orderdemo.mapper") // 扫描Mapper接口 public class OrderDemoApplication { public static void main(String\[\] args) { SpringApplication.run(OrderDemoApplication.class, args); } } |

执行启动类的 main 方法,若控制台无异常日志、输出 "Started OrderDemoApplication" 标识,说明后端基础工程搭建成功。

3.1.3 引入必要依赖

在生成的pom.xml文件中,除了 Spring Initializr 自动引入的依赖外,还需手动补充 MyBatis-Plus、Druid 连接池、Knife4j 接口文档、Jackson 等核心依赖的配置,确保后续业务开发有完整的技术支撑,完整的pom.xml依赖配置示例如下:

|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.5</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>order-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>order-demo</name> <description>订单库存物流示例项目</description> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>17</java.version> <mybatis-plus.version>3.5.3.1</mybatis-plus.version> <druid.version>1.2.16</druid.version> <knife4j.version>4.3.0</knife4j.version> </properties> <dependencies> <!-- Spring Boot Web Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring Boot Validation Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <!-- Spring Boot Data Redis Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- MyBatis-Plus Starter --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>{mybatis-plus.version}\ \ \