feat(shop): 实现商品销量累加和跨租户查询功能

- 添加商品销量累加功能,确保支付成功后更新销量- 实现跨租户查询用户和订单商品的功能
- 修复支付回调中的错误代码
-优化日志记录和异常处理
This commit is contained in:
2025-08-23 03:43:21 +08:00
parent 2a8d87a4d1
commit cccc13df79
25 changed files with 1453 additions and 63 deletions

View File

@@ -0,0 +1,235 @@
# 商品销量累加功能实现
## 🎯 功能概述
实现了商品销售数量的累加功能,确保在支付成功后能够正确更新商品的销量统计。使用`@InterceptorIgnore`注解忽略租户隔离,确保跨租户的商品销量能够正确更新。
## 🔧 实现内容
### 1. ShopGoodsService接口扩展
**文件**: `src/main/java/com/gxwebsoft/shop/service/ShopGoodsService.java`
```java
/**
* 累加商品销售数量
* 忽略租户隔离,确保能更新成功
*
* @param goodsId 商品ID
* @param saleCount 累加的销售数量
* @return 是否更新成功
*/
boolean addSaleCount(Integer goodsId, Integer saleCount);
```
### 2. ShopGoodsMapper数据库操作
**文件**: `src/main/java/com/gxwebsoft/shop/mapper/ShopGoodsMapper.java`
```java
/**
* 累加商品销售数量
* 使用@InterceptorIgnore忽略租户隔离确保能更新成功
*
* @param goodsId 商品ID
* @param saleCount 累加的销售数量
* @return 影响的行数
*/
@InterceptorIgnore(tenantLine = "true")
@Update("UPDATE shop_goods SET sales = IFNULL(sales, 0) + #{saleCount} WHERE goods_id = #{goodsId}")
int addSaleCount(@Param("goodsId") Integer goodsId, @Param("saleCount") Integer saleCount);
```
**关键特性**:
-`@InterceptorIgnore(tenantLine = "true")` - 忽略租户隔离
-`IFNULL(sales, 0)` - 处理销量字段为null的情况
- ✅ 原子性操作 - 直接在数据库层面进行累加
### 3. ShopGoodsServiceImpl业务实现
**文件**: `src/main/java/com/gxwebsoft/shop/service/impl/ShopGoodsServiceImpl.java`
```java
@Override
public boolean addSaleCount(Integer goodsId, Integer saleCount) {
try {
if (goodsId == null || saleCount == null || saleCount <= 0) {
log.warn("累加商品销量参数无效 - 商品ID: {}, 销量: {}", goodsId, saleCount);
return false;
}
int affectedRows = baseMapper.addSaleCount(goodsId, saleCount);
boolean success = affectedRows > 0;
if (success) {
log.info("商品销量累加成功 - 商品ID: {}, 累加数量: {}, 影响行数: {}", goodsId, saleCount, affectedRows);
} else {
log.warn("商品销量累加失败 - 商品ID: {}, 累加数量: {}, 影响行数: {}", goodsId, saleCount, affectedRows);
}
return success;
} catch (Exception e) {
log.error("累加商品销量异常 - 商品ID: {}, 累加数量: {}", goodsId, saleCount, e);
return false;
}
}
```
**功能特性**:
- ✅ 参数验证 - 检查goodsId和saleCount的有效性
- ✅ 异常处理 - 捕获并记录异常信息
- ✅ 详细日志 - 记录操作结果和关键信息
- ✅ 返回值明确 - 明确返回操作是否成功
### 4. ShopOrderServiceImpl集成
**文件**: `src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderServiceImpl.java`
```java
/**
* 累计单个商品的销量
* 使用新的addSaleCount方法忽略租户隔离确保更新成功
*/
private void updateSingleGoodsSales(ShopOrderGoods orderGoods) {
try {
if (orderGoods.getGoodsId() == null || orderGoods.getTotalNum() == null || orderGoods.getTotalNum() <= 0) {
log.warn("商品销量累计参数无效 - 商品ID{},购买数量:{}",
orderGoods.getGoodsId(), orderGoods.getTotalNum());
return;
}
// 使用新的addSaleCount方法忽略租户隔离
boolean updated = shopGoodsService.addSaleCount(orderGoods.getGoodsId(), orderGoods.getTotalNum());
if (updated) {
log.info("商品销量累计成功 - 商品ID{},商品名称:{},购买数量:{}",
orderGoods.getGoodsId(), orderGoods.getGoodsName(), orderGoods.getTotalNum());
} else {
log.warn("商品销量累计失败 - 商品ID{},商品名称:{},购买数量:{}",
orderGoods.getGoodsId(), orderGoods.getGoodsName(), orderGoods.getTotalNum());
}
} catch (Exception e) {
log.error("累计单个商品销量异常 - 商品ID{},商品名称:{},购买数量:{}",
orderGoods.getGoodsId(), orderGoods.getGoodsName(), orderGoods.getTotalNum(), e);
}
}
```
## 🔄 调用流程
```
支付成功回调
ShopOrderServiceImpl.updateByOutTradeNo()
handlePaymentSuccess()
updateGoodsSales()
updateSingleGoodsSales()
ShopGoodsService.addSaleCount()
ShopGoodsMapper.addSaleCount() [忽略租户隔离]
数据库更新销量
```
## 🎯 核心优势
### 1. 租户隔离处理
- ✅ 使用`@InterceptorIgnore(tenantLine = "true")`忽略租户隔离
- ✅ 确保跨租户商品销量能够正确更新
- ✅ 避免因租户隔离导致的更新失败
### 2. 数据一致性
- ✅ 原子性操作 - 在数据库层面直接累加
- ✅ 避免并发问题 - 不需要先查询再更新
- ✅ 处理null值 - 使用IFNULL确保计算正确
### 3. 错误处理
- ✅ 完善的参数验证
- ✅ 异常捕获和日志记录
- ✅ 明确的返回值指示操作结果
### 4. 性能优化
- ✅ 单条SQL语句完成累加
- ✅ 避免查询-修改-更新的多步操作
- ✅ 减少数据库交互次数
## 🧪 测试验证
**测试文件**: `src/test/java/com/gxwebsoft/shop/service/ShopGoodsSalesTest.java`
### 测试用例
1. **基本功能测试** - 验证正常的销量累加
2. **参数验证测试** - 验证各种无效参数的处理
3. **批量累加测试** - 验证多次累加的正确性
### 运行测试
```bash
# 运行单个测试类
mvn test -Dtest=ShopGoodsSalesTest
# 运行特定测试方法
mvn test -Dtest=ShopGoodsSalesTest#testAddSaleCount
```
## 📋 使用示例
```java
// 在支付成功后累加商品销量
@Resource
private ShopGoodsService shopGoodsService;
// 累加销量
Integer goodsId = 123;
Integer purchaseCount = 5;
boolean success = shopGoodsService.addSaleCount(goodsId, purchaseCount);
if (success) {
log.info("商品销量累加成功");
} else {
log.error("商品销量累加失败");
}
```
## 🔍 监控和日志
### 成功日志
```
商品销量累加成功 - 商品ID: 123, 累加数量: 5, 影响行数: 1
```
### 失败日志
```
商品销量累加失败 - 商品ID: 123, 累加数量: 5, 影响行数: 0
累加商品销量参数无效 - 商品ID: null, 销量: 5
```
### 异常日志
```
累加商品销量异常 - 商品ID: 123, 累加数量: 5
```
## ✅ 验证清单
- [x] ShopGoodsService接口添加addSaleCount方法
- [x] ShopGoodsMapper添加数据库操作方法
- [x] 使用@InterceptorIgnore忽略租户隔离
- [x] ShopGoodsServiceImpl实现业务逻辑
- [x] ShopOrderServiceImpl集成新方法
- [x] 添加完善的参数验证和异常处理
- [x] 创建测试用例验证功能
- [x] 添加详细的日志记录
## 🎉 总结
商品销量累加功能已完整实现,具备以下特性:
- **可靠性**: 忽略租户隔离,确保更新成功
- **一致性**: 原子性操作,避免并发问题
- **健壮性**: 完善的错误处理和参数验证
- **可观测性**: 详细的日志记录和监控
- **可测试性**: 完整的测试用例覆盖
现在支付成功后,商品销量能够正确累加,不会因为租户隔离或其他问题导致更新失败。

View File

@@ -0,0 +1,175 @@
# 支付回调代码修复说明
## 🔍 问题描述
在支付回调处理代码中发现了一行红色的错误代码:
```java
shopOrderGoodsService.addSaleCount(order.getOrderGoods());
```
## ❌ 问题原因
1. **方法不存在**`ShopOrderGoodsService`中没有`addSaleCount`方法
2. **参数类型错误**`order.getOrderGoods()`返回的可能是订单商品列表,不是单个商品
3. **重复处理**:销量累加逻辑已经在`ShopOrderServiceImpl.updateByOutTradeNo`中处理了
## ✅ 修复方案
### 删除多余代码
**修复前**
```java
shopOrderService.updateByOutTradeNo(order);
// 6. TODO 累加商品销售数量
shopOrderGoodsService.addSaleCount(order.getOrderGoods());
return "SUCCESS";
```
**修复后**
```java
// 更新订单状态并处理支付成功后的业务逻辑(包括累加商品销量)
shopOrderService.updateByOutTradeNo(order);
return "SUCCESS";
```
## 🔄 正确的销量累加流程
销量累加已经在`ShopOrderServiceImpl`中正确实现:
```
支付回调成功
shopOrderService.updateByOutTradeNo(order)
handlePaymentSuccess(order)
updateGoodsSales(order)
获取订单商品列表shopOrderGoodsService.list(orderId)
遍历每个商品updateSingleGoodsSales(orderGoods)
累加销量shopGoodsService.addSaleCount(goodsId, saleCount)
数据库更新:@InterceptorIgnore 忽略租户隔离
```
## 📋 核心实现代码
### 1. ShopOrderServiceImpl.updateByOutTradeNo
```java
@Override
public void updateByOutTradeNo(ShopOrder order) {
baseMapper.updateByOutTradeNo(order);
// 处理支付成功后的业务逻辑
handlePaymentSuccess(order);
if (order.getTenantId().equals(10550)) {
shopOrderUpdate10550Service.update(order);
}
}
```
### 2. handlePaymentSuccess
```java
private void handlePaymentSuccess(ShopOrder order) {
try {
// 1. 使用优惠券
if (order.getCouponId() != null && order.getCouponId() > 0) {
markCouponAsUsed(order);
}
// 2. 累计商品销量
updateGoodsSales(order);
log.info("支付成功后业务逻辑处理完成 - 订单号:{}", order.getOrderNo());
} catch (Exception e) {
log.error("处理支付成功后业务逻辑失败 - 订单号:{}", order.getOrderNo(), e);
}
}
```
### 3. updateGoodsSales
```java
private void updateGoodsSales(ShopOrder order) {
try {
// 获取订单商品列表
List<ShopOrderGoods> orderGoodsList = shopOrderGoodsService.list(
new LambdaQueryWrapper<ShopOrderGoods>()
.eq(ShopOrderGoods::getOrderId, order.getOrderId())
);
if (orderGoodsList == null || orderGoodsList.isEmpty()) {
log.warn("订单商品列表为空,无法累计销量 - 订单号:{}", order.getOrderNo());
return;
}
// 累计每个商品的销量
for (ShopOrderGoods orderGoods : orderGoodsList) {
updateSingleGoodsSales(orderGoods);
}
log.info("商品销量累计完成 - 订单号:{},商品数量:{}", order.getOrderNo(), orderGoodsList.size());
} catch (Exception e) {
log.error("累计商品销量失败 - 订单号:{}", order.getOrderNo(), e);
}
}
```
### 4. updateSingleGoodsSales
```java
private void updateSingleGoodsSales(ShopOrderGoods orderGoods) {
try {
if (orderGoods.getGoodsId() == null || orderGoods.getTotalNum() == null || orderGoods.getTotalNum() <= 0) {
log.warn("商品销量累计参数无效 - 商品ID{},购买数量:{}",
orderGoods.getGoodsId(), orderGoods.getTotalNum());
return;
}
// 使用新的addSaleCount方法忽略租户隔离
boolean updated = shopGoodsService.addSaleCount(orderGoods.getGoodsId(), orderGoods.getTotalNum());
if (updated) {
log.info("商品销量累计成功 - 商品ID{},商品名称:{},购买数量:{}",
orderGoods.getGoodsId(), orderGoods.getGoodsName(), orderGoods.getTotalNum());
} else {
log.warn("商品销量累计失败 - 商品ID{},商品名称:{},购买数量:{}",
orderGoods.getGoodsId(), orderGoods.getGoodsName(), orderGoods.getTotalNum());
}
} catch (Exception e) {
log.error("累计单个商品销量异常 - 商品ID{},商品名称:{},购买数量:{}",
orderGoods.getGoodsId(), orderGoods.getGoodsName(), orderGoods.getTotalNum(), e);
}
}
```
## ✅ 修复验证
### 1. 编译检查
- ✅ 删除了错误的代码行
- ✅ 不再有红色错误提示
- ✅ 代码可以正常编译
### 2. 功能验证
- ✅ 支付回调正常处理
- ✅ 订单状态正确更新
- ✅ 商品销量正确累加
- ✅ 忽略租户隔离,确保更新成功
### 3. 日志验证
支付成功后会看到以下日志:
```
支付成功后业务逻辑处理完成 - 订单号xxx
商品销量累计完成 - 订单号xxx商品数量2
商品销量累计成功 - 商品ID123商品名称测试商品购买数量1
商品销量累加成功 - 商品ID: 123, 累加数量: 1, 影响行数: 1
```
## 🎯 总结
-**删除了错误代码**`shopOrderGoodsService.addSaleCount(order.getOrderGoods())`
-**保留了正确实现**:通过`shopOrderService.updateByOutTradeNo(order)`自动处理
-**功能完整**:销量累加逻辑已经完整实现并集成到支付流程中
-**租户隔离**:使用`@InterceptorIgnore`确保跨租户更新成功
现在支付回调代码没有错误,销量累加功能正常工作!

View File

@@ -0,0 +1,228 @@
# 用户忽略租户隔离查询功能实现
## 🔍 问题背景
`ShopOrderUpdate10550ServiceImpl.java`需要根据订单的用户ID查询用户信息
```java
final User user = userService.getById(order.getUserId());
```
但是由于租户隔离机制,可能无法查询到其他租户的用户信息,导致业务逻辑失败。
## 🎯 解决方案
实现了一个忽略租户隔离的用户查询方法`getByIdIgnoreTenant`,确保能够跨租户查询用户信息。
## 🔧 实现内容
### 1. UserService接口扩展
**文件**: `src/main/java/com/gxwebsoft/common/system/service/UserService.java`
```java
/**
* 根据用户ID查询用户忽略租户隔离
* @param userId 用户ID
* @return User
*/
User getByIdIgnoreTenant(Integer userId);
```
### 2. UserMapper数据库操作
**文件**: `src/main/java/com/gxwebsoft/common/system/mapper/UserMapper.java`
```java
/**
* 根据用户ID查询用户忽略租户隔离
* @param userId 用户ID
* @return User
*/
@InterceptorIgnore(tenantLine = "true")
User selectByIdIgnoreTenant(@Param("userId") Integer userId);
```
**关键特性**:
-`@InterceptorIgnore(tenantLine = "true")` - 忽略租户隔离
- ✅ 支持跨租户查询用户信息
### 3. UserServiceImpl业务实现
**文件**: `src/main/java/com/gxwebsoft/common/system/service/impl/UserServiceImpl.java`
```java
@Override
public User getByIdIgnoreTenant(Integer userId) {
if (userId == null) {
return null;
}
return baseMapper.selectByIdIgnoreTenant(userId);
}
```
**功能特性**:
- ✅ 参数验证 - 检查userId的有效性
- ✅ 空值处理 - userId为null时返回null
- ✅ 忽略租户隔离 - 可以查询任意租户的用户
### 4. UserMapper.xml SQL映射
**文件**: `src/main/java/com/gxwebsoft/common/system/mapper/xml/UserMapper.xml`
```xml
<!-- 根据用户ID查询用户忽略租户隔离 -->
<select id="selectByIdIgnoreTenant" resultType="com.gxwebsoft.common.system.entity.User">
SELECT a.*,
c.dict_data_name sex_name,
e.tenant_name,
h.dealer_id
FROM gxwebsoft_core.sys_user a
LEFT JOIN (
<include refid="selectSexDictSql"/>
) c ON a.sex = c.dict_data_code
LEFT JOIN gxwebsoft_core.sys_tenant e ON a.tenant_id = e.tenant_id
LEFT JOIN gxwebsoft_core.sys_user_referee h ON a.user_id = h.user_id and h.deleted = 0
WHERE a.user_id = #{userId}
AND a.deleted = 0
</select>
```
**SQL特性**:
- ✅ 完整的用户信息查询(包括关联表)
- ✅ 包含性别字典、租户信息、推荐人信息
- ✅ 只过滤已删除的用户,不过滤租户
### 5. ShopOrderUpdate10550ServiceImpl集成
**文件**: `src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderUpdate10550ServiceImpl.java`
```java
// 修改前(受租户隔离影响)
final User user = userService.getById(order.getUserId());
// 修改后(忽略租户隔离)
final User user = userService.getByIdIgnoreTenant(order.getUserId());
```
## 🔄 使用场景
### 1. 支付回调处理
```java
// 在支付回调中需要查询订单用户信息
final User user = userService.getByIdIgnoreTenant(order.getUserId());
if (user != null) {
// 处理用户相关业务逻辑
log.info("用户信息 - ID: {}, 用户名: {}, 租户: {}",
user.getUserId(), user.getUsername(), user.getTenantId());
}
```
### 2. 跨租户业务处理
```java
// 需要处理其他租户用户的业务
User crossTenantUser = userService.getByIdIgnoreTenant(otherTenantUserId);
if (crossTenantUser != null) {
// 执行跨租户业务逻辑
}
```
## 🎯 核心优势
### 1. 租户隔离绕过
- ✅ 使用`@InterceptorIgnore(tenantLine = "true")`忽略租户隔离
- ✅ 可以查询任意租户的用户信息
- ✅ 不受当前登录用户租户限制
### 2. 数据完整性
- ✅ 查询完整的用户信息(包括关联数据)
- ✅ 包含性别字典、租户信息、推荐人信息
- ✅ 与普通查询返回相同的数据结构
### 3. 安全性考虑
- ✅ 仅在特定业务场景使用
- ✅ 不暴露给前端接口
- ✅ 主要用于内部业务逻辑处理
### 4. 性能优化
- ✅ 单次查询获取完整信息
- ✅ 复用现有的SQL结构
- ✅ 避免多次查询关联数据
## 🧪 测试验证
**测试文件**: `src/test/java/com/gxwebsoft/common/system/service/UserIgnoreTenantTest.java`
### 测试用例
1. **基本功能测试** - 验证忽略租户隔离查询
2. **参数验证测试** - 验证null值和无效ID的处理
3. **跨租户查询测试** - 验证查询不同租户用户的能力
### 运行测试
```bash
# 运行单个测试类
mvn test -Dtest=UserIgnoreTenantTest
# 运行特定测试方法
mvn test -Dtest=UserIgnoreTenantTest#testGetByIdIgnoreTenant
```
## 📋 对比分析
| 方法 | 租户隔离 | 使用场景 | 安全性 |
|-----|---------|----------|--------|
| `getById()` | ✅ 受限制 | 普通业务查询 | 高 |
| `getByIdIgnoreTenant()` | ❌ 忽略 | 跨租户业务处理 | 中等 |
## 🔍 使用注意事项
### 1. 使用场景限制
- 仅在确实需要跨租户查询时使用
- 主要用于内部业务逻辑,不暴露给前端
- 避免在普通的CRUD操作中使用
### 2. 安全考虑
- 确保调用方有合理的业务需求
- 记录关键操作日志
- 避免敏感信息泄露
### 3. 性能考虑
- 查询结果包含关联数据,注意性能影响
- 在高并发场景下谨慎使用
- 考虑添加缓存机制
## 📊 监控和日志
### 使用日志
```java
log.info("跨租户查询用户 - 用户ID: {}, 查询结果: {}",
userId, user != null ? "成功" : "失败");
```
### 业务日志
```java
if (user != null) {
log.info("用户信息 - ID: {}, 用户名: {}, 租户ID: {}",
user.getUserId(), user.getUsername(), user.getTenantId());
}
```
## ✅ 验证清单
- [x] UserService接口添加getByIdIgnoreTenant方法
- [x] UserMapper添加selectByIdIgnoreTenant方法
- [x] 使用@InterceptorIgnore忽略租户隔离
- [x] UserServiceImpl实现业务逻辑
- [x] UserMapper.xml添加SQL映射
- [x] ShopOrderUpdate10550ServiceImpl使用新方法
- [x] 添加参数验证和空值处理
- [x] 创建测试用例验证功能
## 🎉 总结
用户忽略租户隔离查询功能已完整实现,具备以下特性:
- **跨租户能力**: 忽略租户隔离,可查询任意租户用户
- **数据完整性**: 返回完整的用户信息和关联数据
- **安全可控**: 仅在特定业务场景使用,不暴露给前端
- **性能优化**: 单次查询获取完整信息
现在在支付回调等跨租户业务场景中,可以正确查询到用户信息,不会因为租户隔离导致查询失败。

View File

@@ -0,0 +1,239 @@
# 订单商品忽略租户隔离查询功能实现
## 🔍 问题背景
在支付回调处理和商品销量累加过程中,需要查询订单的商品列表:
```java
List<ShopOrderGoods> orderGoodsList = shopOrderGoodsService.getListByOrderId(order.getOrderId());
```
但是由于租户隔离机制,可能无法查询到其他租户的订单商品信息,导致销量累加失败。
## 🎯 解决方案
实现了一个忽略租户隔离的订单商品查询方法`getListByOrderIdIgnoreTenant`,确保能够跨租户查询订单商品信息。
## 🔧 实现内容
### 1. ShopOrderGoodsService接口扩展
**文件**: `src/main/java/com/gxwebsoft/shop/service/ShopOrderGoodsService.java`
```java
/**
* 根据订单ID查询订单商品列表忽略租户隔离
* @param orderId 订单ID
* @return List<ShopOrderGoods>
*/
List<ShopOrderGoods> getListByOrderIdIgnoreTenant(Integer orderId);
```
### 2. ShopOrderGoodsMapper数据库操作
**文件**: `src/main/java/com/gxwebsoft/shop/mapper/ShopOrderGoodsMapper.java`
```java
/**
* 根据订单ID查询订单商品列表忽略租户隔离
* @param orderId 订单ID
* @return List<ShopOrderGoods>
*/
@InterceptorIgnore(tenantLine = "true")
@Select("SELECT * FROM shop_order_goods WHERE order_id = #{orderId} AND deleted = 0")
List<ShopOrderGoods> selectListByOrderIdIgnoreTenant(@Param("orderId") Integer orderId);
```
**关键特性**:
-`@InterceptorIgnore(tenantLine = "true")` - 忽略租户隔离
-`@Select`注解直接执行SQL查询
- ✅ 只过滤已删除的记录,不过滤租户
### 3. ShopOrderGoodsServiceImpl业务实现
**文件**: `src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderGoodsServiceImpl.java`
```java
@Override
public List<ShopOrderGoods> getListByOrderIdIgnoreTenant(Integer orderId) {
try {
if (orderId == null) {
log.warn("查询订单商品列表参数无效 - 订单ID: {}", orderId);
return List.of();
}
List<ShopOrderGoods> orderGoodsList = baseMapper.selectListByOrderIdIgnoreTenant(orderId);
log.info("忽略租户隔离查询订单商品成功 - 订单ID: {}, 商品数量: {}",
orderId, orderGoodsList != null ? orderGoodsList.size() : 0);
return orderGoodsList != null ? orderGoodsList : List.of();
} catch (Exception e) {
log.error("忽略租户隔离查询订单商品异常 - 订单ID: {}", orderId, e);
return List.of();
}
}
```
**功能特性**:
- ✅ 参数验证 - 检查orderId的有效性
- ✅ 异常处理 - 捕获并记录异常信息
- ✅ 详细日志 - 记录查询结果和关键信息
- ✅ 安全返回 - 异常时返回空列表而不是null
### 4. ShopOrderServiceImpl集成
**文件**: `src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderServiceImpl.java`
```java
// 修改前(受租户隔离影响)
final List<ShopOrderGoods> orderGoodsList = shopOrderGoodsService.getListByOrderId(order.getOrderId());
// 修改后(忽略租户隔离)
final List<ShopOrderGoods> orderGoodsList = shopOrderGoodsService.getListByOrderIdIgnoreTenant(order.getOrderId());
```
## 🔄 调用流程
```
支付成功回调
ShopOrderServiceImpl.updateByOutTradeNo()
handlePaymentSuccess()
updateGoodsSales()
shopOrderGoodsService.getListByOrderIdIgnoreTenant() [忽略租户隔离]
获取订单商品列表
updateSingleGoodsSales() [累加每个商品销量]
```
## 🎯 核心优势
### 1. 租户隔离绕过
- ✅ 使用`@InterceptorIgnore(tenantLine = "true")`忽略租户隔离
- ✅ 可以查询任意租户的订单商品信息
- ✅ 确保跨租户业务逻辑正常执行
### 2. 数据完整性
- ✅ 查询完整的订单商品信息
- ✅ 包含商品ID、名称、数量等关键信息
- ✅ 与普通查询返回相同的数据结构
### 3. 错误处理
- ✅ 完善的参数验证
- ✅ 异常捕获和日志记录
- ✅ 安全的返回值处理
### 4. 性能优化
- ✅ 直接SQL查询避免复杂的条件构建
- ✅ 单次查询获取所有订单商品
- ✅ 减少数据库交互次数
## 🧪 测试验证
**测试文件**: `src/test/java/com/gxwebsoft/shop/service/ShopOrderGoodsIgnoreTenantTest.java`
### 测试用例
1. **基本功能测试** - 验证忽略租户隔离查询订单商品
2. **参数验证测试** - 验证null值和无效ID的处理
3. **跨租户查询测试** - 验证查询不同租户订单商品的能力
4. **批量查询性能测试** - 验证批量查询的性能表现
### 运行测试
```bash
# 运行单个测试类
mvn test -Dtest=ShopOrderGoodsIgnoreTenantTest
# 运行特定测试方法
mvn test -Dtest=ShopOrderGoodsIgnoreTenantTest#testGetListByOrderIdIgnoreTenant
```
## 📋 对比分析
| 方法 | 租户隔离 | 使用场景 | 安全性 |
|-----|---------|----------|--------|
| `getListByOrderId()` | ✅ 受限制 | 普通业务查询 | 高 |
| `getListByOrderIdIgnoreTenant()` | ❌ 忽略 | 跨租户业务处理 | 中等 |
## 🔍 使用场景
### 1. 支付回调处理
```java
// 在支付回调中需要查询订单商品进行销量累加
List<ShopOrderGoods> orderGoodsList = shopOrderGoodsService.getListByOrderIdIgnoreTenant(order.getOrderId());
for (ShopOrderGoods orderGoods : orderGoodsList) {
// 累加商品销量
shopGoodsService.addSaleCount(orderGoods.getGoodsId(), orderGoods.getTotalNum());
}
```
### 2. 跨租户订单处理
```java
// 需要处理其他租户订单的商品信息
List<ShopOrderGoods> crossTenantOrderGoods = shopOrderGoodsService.getListByOrderIdIgnoreTenant(otherTenantOrderId);
if (!crossTenantOrderGoods.isEmpty()) {
// 执行跨租户业务逻辑
}
```
## 📊 监控和日志
### 成功日志
```
忽略租户隔离查询订单商品成功 - 订单ID: 123, 商品数量: 3
```
### 失败日志
```
查询订单商品列表参数无效 - 订单ID: null
忽略租户隔离查询订单商品异常 - 订单ID: 123
```
### 业务日志
```java
log.info("订单商品详情 - ID: {}, 商品ID: {}, 商品名称: {}, 数量: {}",
orderGoods.getId(), orderGoods.getGoodsId(),
orderGoods.getGoodsName(), orderGoods.getTotalNum());
```
## 🔒 安全考虑
### 1. 使用场景限制
- 仅在确实需要跨租户查询时使用
- 主要用于内部业务逻辑,不暴露给前端
- 避免在普通的CRUD操作中使用
### 2. 数据安全
- 确保调用方有合理的业务需求
- 记录关键操作日志
- 避免敏感信息泄露
### 3. 性能考虑
- 在高并发场景下谨慎使用
- 考虑添加缓存机制
- 监控查询性能
## ✅ 验证清单
- [x] ShopOrderGoodsService接口添加getListByOrderIdIgnoreTenant方法
- [x] ShopOrderGoodsMapper添加selectListByOrderIdIgnoreTenant方法
- [x] 使用@InterceptorIgnore忽略租户隔离
- [x] ShopOrderGoodsServiceImpl实现业务逻辑
- [x] ShopOrderServiceImpl使用新方法
- [x] 添加完善的参数验证和异常处理
- [x] 创建测试用例验证功能
- [x] 添加详细的日志记录
## 🎉 总结
订单商品忽略租户隔离查询功能已完整实现,具备以下特性:
- **跨租户能力**: 忽略租户隔离,可查询任意租户的订单商品
- **数据完整性**: 返回完整的订单商品信息
- **安全可控**: 仅在特定业务场景使用,不暴露给前端
- **性能优化**: 直接SQL查询高效获取数据
- **错误处理**: 完善的异常处理和日志记录
现在在支付回调等跨租户业务场景中,可以正确查询到订单商品信息,确保商品销量累加功能正常工作,不会因为租户隔离导致查询失败。