如果你是一名Java开发者正在寻找一个能串联起SpringBoot核心知识、贴近真实业务场景的毕业设计或练手项目那么“化妆品销售系统”可能比你想象中更值得投入。很多教程里的“XX管理系统”往往停留在CRUD的层面但一个真正的电商系统从商品展示到订单支付从库存管理到用户评价每一个环节都藏着SpringBoot实战的“硬骨头”。这篇文章不会给你一个简单的、只能增删改查的Demo。我们将一起构建一个功能相对完整、架构清晰、可直接用于学习和参考的SpringBoot化妆品销售系统。我会重点拆解那些在真实开发中必然会遇到的难题如何优雅地设计商品SKU与库存模型如何在高并发下保证库存扣减的准确性如何集成第三方支付并处理回调以及如何利用SpringBoot的缓存、事务、异常处理等特性来提升系统的健壮性。通过这个项目你不仅能得到一个可运行的代码仓库更能掌握一套应对中小型电商系统开发的通用方法论。下面我们就从最核心的问题开始这个系统到底要解决什么以及我们该如何一步步实现它。1. 这篇文章真正要解决的问题为什么选择“化妆品销售系统”作为SpringBoot的实战项目因为它几乎涵盖了Web后端开发中最典型、最复杂的业务场景。一个简单的信息管理后台可能只需要处理单表增删改查。但一个销售系统你必须面对复杂的商品模型化妆品往往有多个规格色号、容量、套装这涉及到SKU库存量单位的设计这是电商系统的基石也是新手最容易设计出错的地方。核心的库存与订单一致性用户下单后库存必须准确扣减同时要生成订单。这个过程涉及数据库事务、并发控制防止超卖是系统稳定性的生命线。多模块的协同用户、商品、购物车、订单、支付、物流模拟这些模块如何划分边界如何通过Service层进行解耦这直接体现了你的业务抽象能力。真实的外部集成虽然我们模拟支付但你需要理解支付回调、异步通知的机制这是与第三方系统交互的通用模式。本文的目标就是带你跨越从“知道SpringBoot注解”到“能用SpringBoot解决复杂业务问题”的鸿沟。你将获得一个结构清晰、包含详细注释的完整项目并理解每个技术决策背后的原因。适合的读者包括正在准备毕业设计的学生、希望深入SpringBoot实战的初级开发者、以及需要快速构建类似业务原型的开发者。2. 基础概念与核心原理在动手编码前我们需要统一几个关键概念这能避免后续设计上的混乱。2.1 系统核心模块划分一个典型的化妆品销售系统可以划分为以下六大模块它们之间的依赖关系构成了系统的主干用户模块 (User)负责注册、登录、个人信息管理、收货地址管理。商品模块 (Product)这是最复杂的部分包含商品分类、品牌、商品SPU标准产品单元、商品SKU具体规格单品、商品图片、商品详情等。购物车模块 (Cart)用户临时存放意向商品的场所需要记录用户、SKU、数量。订单模块 (Order)系统的核心业务单据包含订单主信息、订单项购买了哪些SKU、订单状态流转待付款、待发货、待收货、已完成等。库存模块 (Inventory)与SKU强关联记录可用库存、锁定库存下单未支付、实际库存。扣减库存是高频且需要强一致性的操作。支付模块 (Payment)模拟支付流程处理支付请求、接收模拟支付回调、更新订单状态。2.2 核心数据模型关系ER图关键点理解下表你就掌握了数据库设计的核心实体主要字段关联关系说明用户 (User)id, username, password, phone, avatar一对多地址(Address)、订单(Order)系统主体商品SPUid, name, category_id, brand_id, detail一对多SKU抽象的商品如“XX品牌精华液”商品SKUid, spu_id, specs, price, stock多对一SPU 一对一库存(Inventory)具体规格的商品如“XX精华液-30ml-滋润型”库存 (Inventory)id, sku_id, total, available, locked一对一SKU关键available total - locked购物车项 (CartItem)id, user_id, sku_id, quantity多对一User, SKU订单 (Order)id, user_id, order_no, total_amount, status一对多订单项(OrderItem)order_no需唯一常用雪花算法生成订单项 (OrderItem)id, order_id, sku_id, quantity, price多对一Order, SKU订单快照记录下单时的单价2.3 SpringBoot在本项目中的核心角色SpringBoot不是新技术它是一套“脚手架”和“约定”。在本项目中它主要帮助我们快速启动通过一个main方法即可启动内嵌Tomcat的Web应用。自动配置简化了数据库连接DataSource、MyBatis/Spring Data JPA、事务管理、JSON序列化等繁琐配置。模块化管理通过Service,Repository,Controller等注解清晰分层。简化开发RestController处理RESTful APITransactional管理事务Cacheable实现缓存。3. 环境准备与前置条件请确保你的开发环境满足以下要求。版本号是建议的你可以根据实际情况调整但需要注意版本间的兼容性。JDK: 版本 8 或 11推荐11长期支持版本。检查命令java -versionMaven: 版本 3.6。检查命令mvn -vIDE: IntelliJ IDEA推荐或 Eclipse with STS插件。数据库: MySQL 5.7 或 8.0。我们将使用MySQL作为持久化存储。其他工具: Postman 或 Apifox用于API测试 Git版本管理。3.1 初始化SpringBoot项目使用Spring Initializr https://start.spring.io 或IDE内置的创建向导生成项目骨架。关键依赖选择Spring Web: 构建Web应用包含RESTful API支持。Spring Data JPA或MyBatis Framework: 持久层框架。本文示例将使用MyBatis-Plus国产增强型MyBatis它需要额外配置。MySQL Driver: MySQL数据库驱动。Lombok: 通过注解自动生成Getter/Setter等方法减少样板代码。Validation: 参数校验支持。生成的pom.xml核心依赖部分类似如下以使用MyBatis-Plus为例?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version !-- 选择一个稳定的版本 -- relativePath/ /parent groupIdcom.example/groupId artifactIdcosmetics-sales-system/artifactId version0.0.1-SNAPSHOT/version namecosmetics-sales-system/name description基于SpringBoot的化妆品销售系统/description properties java.version11/java.version mybatis-plus.version3.5.3.1/mybatis-plus.version /properties dependencies !-- SpringBoot Web -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- MyBatis-Plus -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version${mybatis-plus.version}/version /dependency !-- MySQL驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency !-- Lombok -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency !-- 参数校验 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency !-- 测试 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId configuration excludes exclude groupIdorg.projectlombok/groupId artifactIdlombok/artifactId /exclude /excludes /configuration /plugin /plugins /build /project3.2 数据库配置在src/main/resources/application.yml中配置数据库连接等信息server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/cosmetics_db?useUnicodetruecharacterEncodingutf-8useSSLfalseserverTimezoneAsia/Shanghai username: root password: your_password # JPA配置如果使用JPA # jpa: # hibernate: # ddl-auto: update # show-sql: true # MyBatis-Plus 配置 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印SQL生产环境关闭 global-config: db-config: id-type: auto # 主键策略数据库自增 mapper-locations: classpath:mapper/*.xml # XML映射文件位置 # 日志级别 logging: level: com.example.mapper: debug # 打印MyBatis Mapper的SQL请先在MySQL中创建数据库CREATE DATABASE cosmetics_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;4. 核心流程拆解从商品上架到订单完成让我们聚焦最核心的业务流理解代码如何支撑业务。下图展示了用户从浏览到完成订单的关键步骤及对应的后端处理 此处为逻辑描述替代图表商品上架管理员在后台创建SPU和SKU系统初始化对应库存。用户浏览用户查询商品列表、详情系统从数据库或缓存中读取信息。加入购物车用户选择SKU和数量系统在cart表中创建或更新记录。提交订单用户从购物车结算系统核心操作开始 a. 开启数据库事务。 b. 生成唯一订单号创建订单主记录(order)。 c. 遍历购物车项创建订单项(order_item)。 d.关键步骤对每个SKU检查并预扣库存inventory.available减少inventory.locked增加。 e. 清空用户购物车。 f. 事务提交。若任何一步失败事务回滚库存恢复。支付用户支付系统调用模拟支付接口接收回调后将订单状态改为“已支付”并触发后续发货流程模拟。库存最终扣减在支付成功后或“待发货”状态将预扣库存(locked)从总库存(total)中扣除。这是一种“预扣库存”模式能有效防止超卖。5. 完整示例与代码实现我们将实现上述流程中最关键的几个部分商品SKU与库存模型、创建订单与扣减库存。5.1 商品与库存数据模型实现首先定义商品SKU和库存实体。这里使用MyBatis-Plus它提供了通用的BaseMapper简化CRUD操作。// 文件src/main/java/com/example/cosmetics/entity/ProductSku.java package com.example.cosmetics.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import java.math.BigDecimal; import java.time.LocalDateTime; Data TableName(product_sku) public class ProductSku { TableId(type IdType.AUTO) private Long id; private Long spuId; // 关联的SPU ID private String skuCode; // SKU编码唯一 private String specs; // 规格JSON字符串如 {color:红色, volume:30ml} private BigDecimal price; // 销售价使用BigDecimal避免精度丢失 private String mainImage; // 主图 private LocalDateTime createTime; private LocalDateTime updateTime; }// 文件src/main/java/com/example/cosmetics/entity/Inventory.java package com.example.cosmetics.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.Version; import lombok.Data; Data TableName(inventory) public class Inventory { TableId(type IdType.AUTO) private Long id; private Long skuId; // 关联SKU ID唯一 private Integer totalStock; // 总库存 private Integer availableStock; // 可用库存 private Integer lockedStock; // 锁定库存已下单未支付 Version // MyBatis-Plus 乐观锁注解用于并发控制 private Integer version; // 业务方法检查库存是否充足 public boolean isSufficient(Integer requiredQuantity) { return this.availableStock requiredQuantity; } // 业务方法预扣库存 public void lockStock(Integer quantity) { if (!isSufficient(quantity)) { throw new RuntimeException(库存不足); } this.availableStock - quantity; this.lockedStock quantity; } // 业务方法确认扣减库存支付成功后 public void deductStock(Integer quantity) { if (this.lockedStock quantity) { throw new RuntimeException(锁定库存不足无法扣减); } this.lockedStock - quantity; this.totalStock - quantity; } // 业务方法释放锁定库存订单取消或超时 public void releaseStock(Integer quantity) { if (this.lockedStock quantity) { // 实际可能记录日志这里简单处理 quantity this.lockedStock; } this.lockedStock - quantity; this.availableStock quantity; } }对应的Mapper接口非常简单// 文件src/main/java/com/example/cosmetics/mapper/InventoryMapper.java package com.example.cosmetics.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.cosmetics.entity.Inventory; import org.apache.ibatis.annotations.Mapper; Mapper public interface InventoryMapper extends BaseMapperInventory { // 可以使用MyBatis-Plus提供的所有CRUD方法 }5.2 创建订单与库存扣减服务这是系统的核心服务必须保证在事务内执行并且处理并发问题。// 文件src/main/java/com/example/cosmetics/service/impl/OrderServiceImpl.java package com.example.cosmetics.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.example.cosmetics.entity.*; import com.example.cosmetics.mapper.*; import com.example.cosmetics.service.OrderService; import com.example.cosmetics.vo.CartItemVO; import com.example.cosmetics.vo.OrderCreateVO; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; Slf4j Service RequiredArgsConstructor // Lombok注解自动注入final字段 public class OrderServiceImpl implements OrderService { private final OrderMapper orderMapper; private final OrderItemMapper orderItemMapper; private final InventoryMapper inventoryMapper; private final CartItemMapper cartItemMapper; private final ProductSkuMapper skuMapper; Override Transactional(rollbackFor Exception.class) // 声明式事务任何异常都回滚 public String createOrder(Long userId, OrderCreateVO orderCreateVO) { // 1. 参数校验略 // 2. 查询购物车选中项假设前端传入了选中的购物车项ID列表 ListLong cartItemIds orderCreateVO.getCartItemIds(); ListCartItem cartItems cartItemMapper.selectList( new QueryWrapperCartItem() .in(id, cartItemIds) .eq(user_id, userId) ); if (cartItems.isEmpty()) { throw new RuntimeException(购物车中没有选中商品); } // 3. 生成唯一订单号简单示例生产环境建议用雪花算法 String orderNo ORD System.currentTimeMillis() UUID.randomUUID().toString().substring(0, 8).toUpperCase(); // 4. 计算总金额并组装订单项 BigDecimal totalAmount BigDecimal.ZERO; ListOrderItem orderItems cartItems.stream().map(cartItem - { // 查询SKU信息获取实时价格也可用购物车快照价这里用实时价 ProductSku sku skuMapper.selectById(cartItem.getSkuId()); if (sku null) { throw new RuntimeException(商品SKU不存在: cartItem.getSkuId()); } // **关键预扣库存带乐观锁** boolean lockSuccess lockInventory(sku.getId(), cartItem.getQuantity()); if (!lockSuccess) { throw new RuntimeException(商品库存不足或锁定失败: sku.getSkuCode()); } // 构建订单项 OrderItem orderItem new OrderItem(); orderItem.setSkuId(sku.getId()); orderItem.setSkuCode(sku.getSkuCode()); orderItem.setSkuSpecs(sku.getSpecs()); orderItem.setQuantity(cartItem.getQuantity()); orderItem.setPrice(sku.getPrice()); // 记录下单时的价格 orderItem.setItemAmount(sku.getPrice().multiply(BigDecimal.valueOf(cartItem.getQuantity()))); return orderItem; }).collect(Collectors.toList()); // 计算订单总金额 totalAmount orderItems.stream() .map(OrderItem::getItemAmount) .reduce(BigDecimal.ZERO, BigDecimal::add); // 5. 创建订单主记录 Order order new Order(); order.setOrderNo(orderNo); order.setUserId(userId); order.setTotalAmount(totalAmount); order.setStatus(OrderStatusEnum.UNPAID.getCode()); // 枚举待支付 order.setCreateTime(LocalDateTime.now()); orderMapper.insert(order); // 6. 保存订单项并关联订单ID orderItems.forEach(item - { item.setOrderId(order.getId()); orderItemMapper.insert(item); }); // 7. 清空购物车中已下单的商品 cartItemMapper.deleteBatchIds(cartItemIds); log.info(订单创建成功订单号{}, 用户ID{}, orderNo, userId); return orderNo; } /** * 预扣库存使用乐观锁 * param skuId SKU ID * param quantity 数量 * return 是否成功 */ private boolean lockInventory(Long skuId, Integer quantity) { // 使用UpdateWrapper进行条件更新version字段实现乐观锁 UpdateWrapperInventory updateWrapper new UpdateWrapper(); updateWrapper.eq(sku_id, skuId) .ge(available_stock, quantity) // 条件可用库存 需求 .setSql(available_stock available_stock - quantity) .setSql(locked_stock locked_stock quantity); // version version 1 会在更新时自动由MyBatis-Plus处理Version注解 int affectedRows inventoryMapper.update(null, updateWrapper); return affectedRows 0; } // 支付成功后的库存最终扣减方法 Override Transactional(rollbackFor Exception.class) public void confirmOrderAndDeductInventory(String orderNo) { // 1. 查询订单项 Order order orderMapper.selectOne(new QueryWrapperOrder().eq(order_no, orderNo)); ListOrderItem orderItems orderItemMapper.selectList( new QueryWrapperOrderItem().eq(order_id, order.getId()) ); // 2. 遍历订单项扣减最终库存 for (OrderItem item : orderItems) { UpdateWrapperInventory updateWrapper new UpdateWrapper(); updateWrapper.eq(sku_id, item.getSkuId()) .ge(locked_stock, item.getQuantity()) // 锁定库存必须足够 .setSql(locked_stock locked_stock - item.getQuantity()) .setSql(total_stock total_stock - item.getQuantity()); int affectedRows inventoryMapper.update(null, updateWrapper); if (affectedRows 0) { log.error(确认扣减库存失败SKU ID: {}, 订单号: {}, item.getSkuId(), orderNo); // 实际项目中这里可能需要更复杂的补偿或告警机制 throw new RuntimeException(库存扣减异常请人工核查); } } // 3. 更新订单状态为“已支付”或“待发货” order.setStatus(OrderStatusEnum.PAID.getCode()); orderMapper.updateById(order); log.info(订单支付成功库存已扣减订单号{}, orderNo); } }5.3 控制器层API接口示例提供对外的RESTful API。// 文件src/main/java/com/example/cosmetics/controller/OrderController.java package com.example.cosmetics.controller; import com.example.cosmetics.common.Result; import com.example.cosmetics.service.OrderService; import com.example.cosmetics.vo.OrderCreateVO; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; RestController RequestMapping(/api/order) RequiredArgsConstructor public class OrderController { private final OrderService orderService; PostMapping(/create) public ResultString createOrder(RequestBody Validated OrderCreateVO orderCreateVO, HttpServletRequest request) { // 从Token或Session中获取当前用户ID此处简化 Long currentUserId getCurrentUserId(request); // 需要实现此方法 try { String orderNo orderService.createOrder(currentUserId, orderCreateVO); return Result.success(订单创建成功, orderNo); } catch (RuntimeException e) { // 这里应该捕获更具体的业务异常 return Result.error(e.getMessage()); } } PostMapping(/pay/callback) public ResultVoid handlePayCallback(RequestParam String orderNo, RequestParam String payStatus) { // 模拟支付回调实际项目需验证签名等 if (SUCCESS.equals(payStatus)) { orderService.confirmOrderAndDeductInventory(orderNo); return Result.success(支付成功订单状态已更新); } else { // 支付失败可能需要触发订单取消、库存释放等逻辑 return Result.error(支付失败); } } private Long getCurrentUserId(HttpServletRequest request) { // 模拟实际应从JWT Token或Session中解析 // 例如String token request.getHeader(Authorization); return 1L; // 返回测试用户ID } }6. 运行结果与效果验证完成编码后启动项目并进行测试。6.1 启动项目在IDE中直接运行CosmeticsSalesSystemApplication的main方法或在项目根目录下使用Maven命令mvn spring-boot:run看到控制台输出类似Started CosmeticsSalesSystemApplication in 5.123 seconds即表示启动成功。6.2 数据库表初始化你需要手动创建相关的表或使用Flyway/Liquibase等数据库迁移工具。这里提供核心表的SQL脚本-- 商品SKU表 CREATE TABLE product_sku ( id bigint NOT NULL AUTO_INCREMENT, spu_id bigint DEFAULT NULL COMMENT SPU ID, sku_code varchar(64) NOT NULL COMMENT SKU编码唯一, specs varchar(500) DEFAULT NULL COMMENT 规格JSON, price decimal(10,2) NOT NULL COMMENT 价格, main_image varchar(255) DEFAULT NULL COMMENT 主图, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_sku_code (sku_code), KEY idx_spu_id (spu_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci; -- 库存表关键 CREATE TABLE inventory ( id bigint NOT NULL AUTO_INCREMENT, sku_id bigint NOT NULL COMMENT 关联SKU ID唯一, total_stock int NOT NULL DEFAULT 0 COMMENT 总库存, available_stock int NOT NULL DEFAULT 0 COMMENT 可用库存, locked_stock int NOT NULL DEFAULT 0 COMMENT 锁定库存, version int NOT NULL DEFAULT 0 COMMENT 乐观锁版本号, PRIMARY KEY (id), UNIQUE KEY uk_sku_id (sku_id), KEY idx_sku_id (sku_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci; -- 订单表 CREATE TABLE order ( id bigint NOT NULL AUTO_INCREMENT, order_no varchar(32) NOT NULL COMMENT 订单号唯一, user_id bigint NOT NULL, total_amount decimal(10,2) NOT NULL COMMENT 订单总金额, status tinyint NOT NULL COMMENT 订单状态, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_user_id (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci; -- 订单项表 CREATE TABLE order_item ( id bigint NOT NULL AUTO_INCREMENT, order_id bigint NOT NULL, sku_id bigint NOT NULL, sku_code varchar(64) DEFAULT NULL, sku_specs varchar(500) DEFAULT NULL, quantity int NOT NULL, price decimal(10,2) NOT NULL COMMENT 下单时单价, item_amount decimal(10,2) NOT NULL COMMENT 单项总价, PRIMARY KEY (id), KEY idx_order_id (order_id), KEY idx_sku_id (sku_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci;6.3 使用Postman测试API插入测试数据手动或在代码中编写初始化脚本向product_sku和inventory表插入数据。例如插入一个SKU ID为1库存为100的商品。测试创建订单APIURL:POST http://localhost:8080/api/order/createBody (JSON):{ cartItemIds: [1, 2] // 假设的购物车项ID }Headers:Content-Type: application/json预期结果返回成功包含订单号。检查数据库order表新增一条状态为“待支付”的记录。order_item表新增对应记录。inventory表中对应SKU的available_stock减少locked_stock增加。测试支付回调APIURL:POST http://localhost:8080/api/order/pay/callback?orderNoORD...payStatusSUCCESS预期结果返回成功。检查数据库order表状态更新为“已支付”。inventory表中对应SKU的locked_stock减少total_stock减少。7. 常见问题与排查思路在开发和运行过程中你可能会遇到以下问题问题现象可能原因排查方式解决方案启动报错Failed to configure a DataSource数据库连接配置错误或驱动未加载。1. 检查application.yml中的url,username,password。2. 检查MySQL服务是否启动。3. 检查网络连通性。修正配置确保数据库可访问。创建订单时提示“库存不足”但数据库有库存。1. 并发场景下乐观锁更新失败。2. 查询库存和扣减库存非原子操作。1. 查看日志中lockInventory方法的返回值。2. 使用JMeter等工具模拟并发请求观察现象。确保使用带版本号的乐观锁更新如示例代码或使用更严格的悲观锁SELECT ... FOR UPDATE。订单创建成功但购物车未清空。事务未正确回滚或在清空购物车前发生异常。1. 检查Transactional注解是否生效方法是否为public。2. 检查异常是否被捕获而未抛出。确保事务注解生效并在发生业务异常时抛出RuntimeException或其子类。API返回404或405错误。1. 请求路径错误。2. 请求方法GET/POST不匹配。3. Controller未正确扫描。1. 检查Postman中的URL和方法。2. 检查RestController和RequestMapping注解。3. 检查启动类包扫描范围。修正请求确保Controller在Spring Boot主应用类的同级或子包下。插入中文数据到数据库乱码。数据库、连接字符串、表三者的字符集不统一。1. 检查MySQL数据库、表、字段的字符集是否为utf8mb4。2. 检查JDBC连接URL是否包含characterEncodingutf-8。统一设置为utf8mb4连接URL添加参数。Autowired注入失败提示Bean找不到。1. 类未被Spring管理缺少Service,Component等。2. 包路径不在组件扫描范围内。1. 检查依赖类是否有注解。2. 检查启动类SpringBootApplication的扫描范围。添加正确的注解或将类移动到启动类的子包下。8. 最佳实践与工程建议将项目跑起来只是第一步要让其具备可维护性和扩展性还需要遵循一些最佳实践。8.1 分层架构与包结构建议采用清晰的分层例如com.example.cosmetics ├── application # 启动类 ├── controller # 控制层处理HTTP请求和响应 ├── service # 业务逻辑层 │ ├── impl # 服务实现类 ├── mapper # 数据访问层MyBatis Mapper接口 ├── entity # 实体类与数据库表对应 ├── vo # 视图对象用于前后端数据交互如请求VO响应VO ├── dto # 数据传输对象可选用于服务间调用 ├── common # 通用类常量、枚举、工具类、统一返回结果等 └── config # 配置类如WebConfig, MyBatisPlusConfig8.2 统一响应封装与异常处理使用一个统一的Result类包装所有API响应并利用ControllerAdvice进行全局异常处理。// 文件src/main/java/com/example/cosmetics/common/Result.java package com.example.cosmetics.common; import lombok.Data; import java.io.Serializable; Data public class ResultT implements Serializable { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(success); result.setData(data); return result; } public static T ResultT success(String message, T data) { ResultT result new Result(); result.setCode(200); result.setMessage(message); result.setData(data); return result; } public static T ResultT error(String message) { ResultT result new Result(); result.setCode(500); result.setMessage(message); return result; } // 可以定义更多错误码 }8.3 使用枚举管理状态码避免在代码中硬编码魔法数字。// 文件src/main/java/com/example/cosmetics/common/enums/OrderStatusEnum.java package com.example.cosmetics.common.enums; import lombok.Getter; Getter public enum OrderStatusEnum { UNPAID(0, 待支付), PAID(1, 已支付), SHIPPED(2, 已发货), RECEIVED(3, 已收货), CANCELLED(4, 已取消), ; private final Integer code; private final String desc; OrderStatusEnum(Integer code, String desc) { this.code code; this.desc desc; } }8.4 接口幂等性与防重对于创建订单、支付回调等关键接口需要防止重复提交。常见方案前端防重提交按钮置灰。Token机制提交前从服务端获取一个唯一Token提交时携带。数据库唯一索引订单号唯一。幂等表记录已处理请求的唯一ID如支付流水号。8.5 性能与扩展考虑缓存对商品详情、分类等读多写少的数据使用Redis缓存。Spring Boot可以轻松集成spring-boot-starter-data-redis并使用Cacheable注解。异步处理对于发短信、记日志等非核心操作可以使用Async注解或消息队列如RabbitMQ, RocketMQ异步处理提升主流程响应速度。分库分表当单表数据量巨大时如订单表需要考虑分库分表方案可使用ShardingSphere等中间件。监控与日志集成Spring Boot Actuator暴露健康检查端点使用SLF4JLogback规范打印日志便于问题排查。9. 总结与后续学习方向通过这个“化妆品销售系统”项目我们完整地实践了一个基于Spring Boot的电商核心模块开发。你不仅学会了如何创建实体、Mapper、Service和Controller更重要的是理解了库存扣减与订单创建的事务一致性、使用乐观锁处理并发、清晰的业务分层等核心开发思想。这个项目是一个强大的起点你可以在此基础上继续深化前端集成使用Vue.js或React构建管理后台和用户商城页面实现前后端分离。搜索功能集成Elasticsearch实现商品的高性能全文检索和复杂筛选。秒杀场景针对高并发秒杀深入探索Redis预减库存、令牌桶限流、队列削峰等方案。分布式事务如果库存服务和订单服务拆分为两个微服务如何保证数据一致性可以研究Seata的AT模式或TCC模式。容器化部署编写Dockerfile将应用打包成Docker镜像并使用Docker Compose或Kubernetes进行部署。建议你将本文的代码作为骨架根据实际需求填充用户管理、商品分类、图片上传、物流查询等更多模块。在编码过程中多思考“如果线上出问题我该如何快速定位”、“这个设计能否应对未来业务的变化”这样的思维训练比单纯写代码更有价值。