# 订单商品验证功能指南 ## 概述 本文档介绍了订单创建时商品信息后台验证的实现方案。该方案确保了订单数据的安全性和一致性,防止前端数据被篡改。 ## 主要特性 ### 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. **可维护性**:清晰的代码结构和完整的测试覆盖 这种方案比前端传递商品信息更安全可靠,是电商系统的最佳实践。