193 lines
5.9 KiB
Markdown
193 lines
5.9 KiB
Markdown
# 订单商品验证功能指南
|
||
|
||
## 概述
|
||
|
||
本文档介绍了订单创建时商品信息后台验证的实现方案。该方案确保了订单数据的安全性和一致性,防止前端数据被篡改。
|
||
|
||
## 主要特性
|
||
|
||
### 1. 商品信息后台验证
|
||
- ✅ 商品存在性验证
|
||
- ✅ 商品状态验证(上架/下架)
|
||
- ✅ 商品价格验证
|
||
- ✅ 库存数量验证
|
||
- ✅ 购买数量限制验证
|
||
- ✅ 订单总金额重新计算
|
||
|
||
### 2. 数据安全保障
|
||
- ✅ 防止前端价格篡改
|
||
- ✅ 使用后台查询的真实商品信息
|
||
- ✅ 订单金额一致性检查
|
||
- ✅ 详细的错误提示信息
|
||
|
||
## 实现细节
|
||
|
||
### 核心方法
|
||
|
||
#### 1. validateOrderRequest()
|
||
```java
|
||
private void validateOrderRequest(OrderCreateRequest request, User loginUser) {
|
||
// 1. 用户登录验证
|
||
if (loginUser == null) {
|
||
throw new BusinessException("用户未登录");
|
||
}
|
||
|
||
// 2. 验证商品信息并计算总金额
|
||
BigDecimal calculatedTotal = validateAndCalculateTotal(request);
|
||
|
||
// 3. 检查金额一致性
|
||
if (request.getTotalPrice() != null &&
|
||
request.getTotalPrice().subtract(calculatedTotal).abs().compareTo(new BigDecimal("0.01")) > 0) {
|
||
throw new BusinessException("订单金额计算错误,请刷新重试");
|
||
}
|
||
|
||
// 4. 使用后台计算的金额
|
||
request.setTotalPrice(calculatedTotal);
|
||
|
||
// 5. 检查租户特殊规则
|
||
// ...
|
||
}
|
||
```
|
||
|
||
#### 2. validateAndCalculateTotal()
|
||
```java
|
||
private BigDecimal validateAndCalculateTotal(OrderCreateRequest request) {
|
||
BigDecimal total = BigDecimal.ZERO;
|
||
|
||
for (OrderCreateRequest.OrderGoodsItem item : request.getGoodsItems()) {
|
||
// 获取商品信息
|
||
ShopGoods goods = shopGoodsService.getById(item.getGoodsId());
|
||
|
||
// 验证商品存在性
|
||
if (goods == null) {
|
||
throw new BusinessException("商品不存在,商品ID:" + item.getGoodsId());
|
||
}
|
||
|
||
// 验证商品状态
|
||
if (goods.getStatus() != 0) {
|
||
throw new BusinessException("商品已下架:" + goods.getName());
|
||
}
|
||
|
||
// 验证库存
|
||
if (goods.getStock() != null && goods.getStock() < item.getQuantity()) {
|
||
throw new BusinessException("商品库存不足:" + goods.getName());
|
||
}
|
||
|
||
// 验证购买数量限制
|
||
if (goods.getCanBuyNumber() != null && item.getQuantity() > goods.getCanBuyNumber()) {
|
||
throw new BusinessException("商品购买数量超过限制:" + goods.getName());
|
||
}
|
||
|
||
// 计算小计
|
||
BigDecimal itemTotal = goods.getPrice().multiply(new BigDecimal(item.getQuantity()));
|
||
total = total.add(itemTotal);
|
||
}
|
||
|
||
return total;
|
||
}
|
||
```
|
||
|
||
### 订单商品保存优化
|
||
|
||
```java
|
||
private void saveOrderGoods(OrderCreateRequest request, ShopOrder shopOrder) {
|
||
for (OrderCreateRequest.OrderGoodsItem item : request.getGoodsItems()) {
|
||
// 重新获取商品信息(确保数据一致性)
|
||
ShopGoods goods = shopGoodsService.getById(item.getGoodsId());
|
||
|
||
// 再次验证商品状态(防止并发问题)
|
||
if (goods.getStatus() != 0) {
|
||
throw new BusinessException("商品已下架:" + goods.getName());
|
||
}
|
||
|
||
ShopOrderGoods orderGoods = new ShopOrderGoods();
|
||
|
||
// 使用后台查询的真实数据
|
||
orderGoods.setGoodsId(item.getGoodsId());
|
||
orderGoods.setGoodsName(goods.getName());
|
||
orderGoods.setPrice(goods.getPrice()); // 使用后台价格
|
||
orderGoods.setTotalNum(item.getQuantity());
|
||
|
||
// 设置其他信息...
|
||
}
|
||
}
|
||
```
|
||
|
||
## 验证流程
|
||
|
||
```mermaid
|
||
graph TD
|
||
A[前端提交订单] --> B[validateOrderRequest]
|
||
B --> C[validateAndCalculateTotal]
|
||
C --> D[验证商品存在性]
|
||
D --> E[验证商品状态]
|
||
E --> F[验证库存数量]
|
||
F --> G[验证购买限制]
|
||
G --> H[计算总金额]
|
||
H --> I[检查金额一致性]
|
||
I --> J[保存订单]
|
||
J --> K[saveOrderGoods]
|
||
K --> L[再次验证商品状态]
|
||
L --> M[使用后台数据保存]
|
||
```
|
||
|
||
## 错误处理
|
||
|
||
### 常见错误信息
|
||
|
||
| 错误码 | 错误信息 | 说明 |
|
||
|--------|----------|------|
|
||
| 1 | 用户未登录 | 用户身份验证失败 |
|
||
| 1 | 商品不存在,商品ID:xxx | 商品ID无效或已删除 |
|
||
| 1 | 商品已下架:xxx | 商品状态不是上架状态 |
|
||
| 1 | 商品价格异常:xxx | 商品价格为空或小于等于0 |
|
||
| 1 | 商品库存不足:xxx,当前库存:xxx | 购买数量超过可用库存 |
|
||
| 1 | 商品购买数量超过限制:xxx,最大购买数量:xxx | 超过单次购买限制 |
|
||
| 1 | 订单金额计算错误,请刷新重试 | 前端金额与后台计算不一致 |
|
||
| 1 | 商品金额不能为0 | 计算后的总金额为0或负数 |
|
||
|
||
## 测试用例
|
||
|
||
项目包含完整的单元测试,覆盖以下场景:
|
||
|
||
1. ✅ 正常订单创建
|
||
2. ✅ 商品不存在
|
||
3. ✅ 商品已下架
|
||
4. ✅ 库存不足
|
||
5. ✅ 超过购买限制
|
||
6. ✅ 多商品金额计算
|
||
7. ✅ 金额不一致检测
|
||
|
||
运行测试:
|
||
```bash
|
||
mvn test -Dtest=OrderValidationTest
|
||
```
|
||
|
||
## 最佳实践
|
||
|
||
### 1. 安全性
|
||
- 始终使用后台查询的商品信息
|
||
- 不信任前端传入的价格数据
|
||
- 在保存前再次验证商品状态
|
||
|
||
### 2. 性能优化
|
||
- 批量查询商品信息(如果需要)
|
||
- 缓存商品基础信息(可选)
|
||
- 合理的错误提示,避免过多数据库查询
|
||
|
||
### 3. 用户体验
|
||
- 提供清晰的错误提示信息
|
||
- 支持金额小误差容忍(0.01元)
|
||
- 及时更新前端商品状态
|
||
|
||
## 总结
|
||
|
||
通过后台验证商品信息的方案,我们实现了:
|
||
|
||
1. **数据安全性**:防止价格篡改,确保订单金额准确
|
||
2. **业务完整性**:完整的商品状态、库存、限制验证
|
||
3. **系统稳定性**:详细的错误处理和日志记录
|
||
4. **可维护性**:清晰的代码结构和完整的测试覆盖
|
||
|
||
这种方案比前端传递商品信息更安全可靠,是电商系统的最佳实践。
|