'# 基于Java的蛋糕商城系统(源代码+数据库+6000字报告)
一、背景与问题
在电商系统开发中,订单管理是核心模块之一。蛋糕商城系统面临以下典型问题:
- 并发控制:高并发场景下库存超卖问题
- 分布式事务:订单创建与库存扣减需要保持一致性
- 支付回调处理:需保证支付回调的幂等性
- 性能瓶颈:高频读写操作对数据库的压力
- 安全风险:SQL注入、XSS攻击等潜在威胁
传统解决方案往往通过数据库事务、锁机制和队列处理来解决这些问题,但需要深入理解其工作原理和实现细节。本文将通过一个完整的蛋糕商城系统案例,深入探讨Java技术栈在电商系统中的应用。
二、基本原理
1. 系统架构设计
采用经典的MVC架构,分为三层:
- 表现层(Controller):处理HTTP请求
- 业务层(Service):实现核心业务逻辑
- 数据层(DAO):数据库操作
系统核心流程:
用户请求 → Controller → Service(订单创建/库存扣减) → DAO(数据库操作)2. 核心技术栈
- Spring Boot:快速构建微服务
- MyBatis:ORM框架
- Redis:缓存与分布式锁
- MySQL:关系型数据库
- RabbitMQ:消息队列
3. 关键技术原理
(1) 分布式锁机制
在分布式环境中,需要保证多个服务实例对共享资源的访问一致性。使用Redis的SETNX命令实现分布式锁:
public boolean tryLock(String lockKey, String requestId, int expireTime) {
String result = redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireTime, TimeUnit.SECONDS);
return result != null;
}(2) 事务传播机制
在Spring中通过@Transactional注解实现事务控制,支持以下传播行为:
REQUIRED(默认):如果当前没有事务,新建事务NEVER:必须没有事务SUPPORTS:支持事务,但可以无事务
(3) 异步处理机制
通过@Async注解实现异步处理,避免阻塞主线程:
@Async
public void asyncProcessOrder(Order order) {
// 扣减库存、更新订单状态等耗时操作
}三、环境准备
1. 开发环境
- Java 17
- Spring Boot 3.x
- MySQL 8.0
- Redis 6.2
- RabbitMQ 3.9
2. 依赖配置(pom.xml)
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>3. 数据库设计
创建三个核心表:
用户表(users)
CREATE TABLE users (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) UNIQUE NOT NULL,
password VARCHAR(100) NOT NULL,
email VARCHAR(100),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);商品表(products)
CREATE TABLE products (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
price DECIMAL(10,2) NOT NULL,
stock INT NOT NULL DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);订单表(orders)
CREATE TABLE orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
product_id BIGINT NOT NULL,
quantity INT NOT NULL,
status ENUM('PENDING', 'PAID', 'SHIPPED', 'COMPLETED') NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);四、核心实现
1. 订单创建流程
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private ProductRepository productRepository;
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
private RabbitTemplate rabbitTemplate;
@Transactional
public void createOrder(Long userId, Long productId, int quantity) {
// 1. 查询商品信息
Product product = productRepository.findById(productId).orElseThrow(() ->
new RuntimeException("Product not found"));
// 2. 检查库存
if (product.getStock() < quantity) {
throw new RuntimeException("Insufficient stock");
}
// 3. 生成订单
Order order = new Order();
order.setUserId(userId);
order.setProductId(productId);
order.setQuantity(quantity);
order.setStatus("PENDING");
// 4. 扣减库存(使用分布式锁)
String lockKey = "product_stock_lock_" + productId;
String requestId = UUID.randomUUID().toString();
if (tryLock(lockKey, requestId, 30)) {
try {
product.setStock(product.getStock() - quantity);
productRepository.save(product);
// 5. 保存订单
orderRepository.save(order);
// 6. 发送消息到队列
rabbitTemplate.convertAndSend("order_queue", order);
} finally {
unlock(lockKey, requestId);
}
} else {
throw new RuntimeException("Failed to acquire lock");
}
}
private boolean tryLock(String lockKey, String requestId, int expireTime) {
String result = redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireTime, TimeUnit.SECONDS);
return result != null;
}
private void unlock(String lockKey, String requestId) {
String currentValue = redisTemplate.opsForValue().get(lockKey);
if (currentValue != null && currentValue.equals(requestId)) {
redisTemplate.delete(lockKey);
}
}
}关键点解释:
- 使用分布式锁确保库存扣减的原子性
- 通过事务保证订单创建和库存更新的原子性
- 异步处理订单状态更新,避免阻塞主线程
2. 支付回调处理
@RestController
public class PayCallbackController {
@Autowired
private OrderService orderService;
@PostMapping("/pay/callback")
public ResponseEntity<String> handlePaymentCallback(@RequestBody PaymentResponse payment) {
try {
// 1. 验证签名
if (!validateSignature(payment)) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Invalid signature");
}
// 2. 更新订单状态
orderService.updateOrderStatus(payment.getOrderId(), "PAID");
// 3. 发送消息到队列
rabbitTemplate.convertAndSend("order_status_queue", payment);
return ResponseEntity.ok("Payment processed successfully");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Processing failed");
}
}
private boolean validateSignature(PaymentResponse payment) {
// 实现签名验证逻辑
return true;
}
}3. 消息队列处理
@Service
public class OrderMessageListener {
@Autowired
private OrderService orderService;
@RabbitListener(queues = "order_queue")
public void handleOrderMessage(String message) {
try {
// 1. 解析消息
Order order = parseMessage(message);
// 2. 更新订单状态
orderService.updateOrderStatus(order.getId(), "SHIPPED");
} catch (Exception e) {
// 处理异常,记录日志
}
}
private Order parseMessage(String message) {
// 实现消息解析逻辑
return new Order();
}
}五、完整案例
1. 系统流程图
用户请求 → Controller → OrderService → Redis锁 → DB库存扣减 → RabbitMQ队列 → OrderMessageListener → DB订单状态更新2. 完整代码示例(订单创建流程)
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
@PostMapping("/orders")
public ResponseEntity<String> createOrder(@RequestParam Long userId,
@RequestParam Long productId,
@RequestParam int quantity) {
try {
orderService.createOrder(userId, productId, quantity);
return ResponseEntity.ok("Order created successfully");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Order creation failed");
}
}
}3. 测试用例
@SpringBootTest
public class OrderServiceTest {
@Autowired
private OrderService orderService;
@Test
public void testCreateOrder() {
// 模拟商品数据
Product product = new Product();
product.setId(1L);
product.setName("Cheesecake");
product.setPrice(129.99);
product.setStock(100);
productRepository.save(product);
// 创建订单
orderService.createOrder(1L, 1L, 2);
// 验证库存
Product updatedProduct = productRepository.findById(1L).orElse(null);
assertNotNull(updatedProduct);
assertEquals(98, updatedProduct.getStock());
}
}六、源码解析
1. 分布式锁实现原理
Redis的SETNX命令在Redis 2.6.12版本后被SET命令取代,支持更多参数。关键点:
NX选项确保只有当键不存在时才设置成功EX选项设置过期时间,防止死锁- 使用UUID作为锁的唯一标识,避免误删
2. 事务传播机制
Spring的@Transactional注解通过AOP实现事务管理,关键点:
- 通过
TransactionDefinition定义事务属性 - 使用
PlatformTransactionManager管理事务 - 支持多种传播行为(如
REQUIRED、NEVER等)
3. 异步处理机制
@Async注解通过AsyncConfigurerSupport实现异步处理,关键点:
- 需要配置
@EnableAsync注解 - 通过
TaskExecutor执行异步任务 - 支持多种任务执行器(如线程池、异步Servlet等)
七、进阶使用
1. 分库分表策略
对于百万级订单数据,可采用分库分表策略:
- 按用户ID分库
- 按订单ID分表
- 使用一致性哈希算法分配数据
2. 缓存策略优化
- 热点商品缓存:使用Redis缓存商品信息
- 预热缓存:在系统启动时加载常用数据
- 缓存更新策略:采用"写时更新"或"读时更新"
3. 安全增强
- 使用JWT实现无状态身份验证
- 防止SQL注入:使用预编译语句
- 防止XSS攻击:对用户输入进行过滤
- 防止CSRF攻击:使用token验证
八、性能与工程实践
1. 性能优化策略
| 优化点 | 解决方案 | 效果 |
|---|---|---|
| 库存扣减 | 分布式锁 + 乐观锁 | 避免超卖 |
| 订单查询 | 缓存 + 索引 | 提升查询速度 |
| 高并发 | 异步处理 + 消息队列 | 分流请求 |
| 数据库 | 读写分离 + 分库分表 | 提升吞吐量 |
2. 异常处理机制
- 使用
@ControllerAdvice全局异常处理 - 记录异常日志:使用SLF4J + Logback
- 返回统一错误格式:JSON格式的错误响应
3. 安全防护措施
- 使用HTTPS加密传输
- 防止SQL注入:使用MyBatis的
#{}占位符 - 防止XSS攻击:对用户输入进行过滤
- 防止CSRF攻击:使用token验证
九、常见问题与踩坑
1. 并发库存超卖问题
错误示例:
public void decreaseStock(int quantity) {
product.setStock(product.getStock() - quantity);
}问题分析: 未使用锁导致并发修改,出现负库存
解决方案: 使用分布式锁 + 乐观锁
2. 支付回调重复处理
错误示例:
public void handlePaymentCallback(PaymentResponse payment) {
orderService.updateOrderStatus(payment.getOrderId(), "PAID");
}问题分析: 支付系统可能重复发送回调
解决方案: 使用幂等性校验
public void handlePaymentCallback(PaymentResponse payment) {
if (isDuplicatePayment(payment.getOrderId())) {
return;
}
orderService.updateOrderStatus(payment.getOrderId(), "PAID");
}3. 分布式锁死锁问题
错误示例:
public void lock() {
String lockKey = "product_stock_lock_" + productId;
String requestId = UUID.randomUUID().toString();
String result = redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, 30, TimeUnit.SECONDS);
if (result == null) {
throw new RuntimeException("Failed to acquire lock");
}
}问题分析: 未处理锁释放逻辑,导致死锁
解决方案: 添加锁释放逻辑
public void lock() {
String lockKey = "product_stock_lock_" + productId;
String requestId = UUID.randomUUID().toString();
String result = redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, 30, TimeUnit.SECONDS);
if (result == null) {
throw new RuntimeException("Failed to acquire lock");
}
try {
// 业务逻辑
} finally {
String currentValue = redisTemplate.opsForValue().get(lockKey);
if (currentValue != null && currentValue.equals(requestId)) {
redisTemplate.delete(lockKey);
}
}
}十、最佳实践
1. 推荐方案
- 订单创建:使用分布式锁 + 事务保证一致性
- 库存管理:采用乐观锁,结合缓存提升性能
- 支付回调:实现幂等性校验,防止重复处理
- 消息队列:使用RabbitMQ处理异步任务
- 安全防护:使用JWT + HTTPS + 输入过滤
2. 不推荐场景
- 高并发写操作:不建议直接操作数据库,应使用队列异步处理
- 数据一致性要求低:可以考虑使用最终一致性模型
- 简单场景:对于小规模系统,可以简化分布式锁机制
十一、总结
本文通过一个完整的蛋糕商城系统案例,深入探讨了Java技术栈在电商系统中的应用。重点分析了分布式锁、事务管理、异步处理等核心技术的实现原理,并结合真实开发场景给出了最佳实践。在开发过程中需要注意以下几点:
- 分布式锁:确保多实例环境下的资源一致性
- 事务管理:保证关键业务操作的原子性
- 异步处理:提高系统吞吐量
- 安全防护:防止常见攻击方式
- 性能优化:通过缓存、分库分表等手段提升性能
在实际开发中,要根据业务需求选择合适的架构方案。对于高并发、分布式系统,需要更复杂的解决方案,如使用分布式事务框架(如Seata)或引入微服务架构。而对小型系统,可以采用简化版的方案,注重开发效率和可维护性。