Commit e6fecd8efeb674473b560df64c1f357c030a1e2a

Authored by 芋道源码
Committed by GitHub
2 parents af2a0c22 3b2a3dd0

Merge pull request #869 from YunaiV/fix-erp-statistics-tenant-issue

fix: ERP统计查询在多租户关闭时的NullPointerException问题
yudao-module-erp/src/main/resources/mapper/statistics/ErpPurchaseStatisticsMapper.xml
@@ -10,7 +10,9 @@ @@ -10,7 +10,9 @@
10 <if test="endTime != null"> 10 <if test="endTime != null">
11 AND in_time &lt; #{endTime} 11 AND in_time &lt; #{endTime}
12 </if> 12 </if>
13 - AND tenant_id = ${@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getRequiredTenantId()} 13 + <if test="@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getTenantId() != null">
  14 + AND tenant_id = ${@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getTenantId()}
  15 + </if>
14 AND deleted = 0) - 16 AND deleted = 0) -
15 (SELECT IFNULL(SUM(total_price), 0) 17 (SELECT IFNULL(SUM(total_price), 0)
16 FROM erp_purchase_return 18 FROM erp_purchase_return
@@ -18,7 +20,9 @@ @@ -18,7 +20,9 @@
18 <if test="endTime != null"> 20 <if test="endTime != null">
19 AND return_time &lt; #{endTime} 21 AND return_time &lt; #{endTime}
20 </if> 22 </if>
21 - AND tenant_id = ${@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getRequiredTenantId()} 23 + <if test="@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getTenantId() != null">
  24 + AND tenant_id = ${@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getTenantId()}
  25 + </if>
22 AND deleted = 0) 26 AND deleted = 0)
23 </select> 27 </select>
24 28
yudao-module-erp/src/main/resources/mapper/statistics/ErpSaleStatisticsMapper.xml
@@ -10,7 +10,9 @@ @@ -10,7 +10,9 @@
10 <if test="endTime != null"> 10 <if test="endTime != null">
11 AND out_time &lt; #{endTime} 11 AND out_time &lt; #{endTime}
12 </if> 12 </if>
13 - AND tenant_id = ${@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getRequiredTenantId()} 13 + <if test="@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getTenantId() != null">
  14 + AND tenant_id = ${@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getTenantId()}
  15 + </if>
14 AND deleted = 0) - 16 AND deleted = 0) -
15 (SELECT IFNULL(SUM(total_price), 0) 17 (SELECT IFNULL(SUM(total_price), 0)
16 FROM erp_sale_return 18 FROM erp_sale_return
@@ -18,7 +20,9 @@ @@ -18,7 +20,9 @@
18 <if test="endTime != null"> 20 <if test="endTime != null">
19 AND return_time &lt; #{endTime} 21 AND return_time &lt; #{endTime}
20 </if> 22 </if>
21 - AND tenant_id = ${@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getRequiredTenantId()} 23 + <if test="@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getTenantId() != null">
  24 + AND tenant_id = ${@cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder@getTenantId()}
  25 + </if>
22 AND deleted = 0) 26 AND deleted = 0)
23 </select> 27 </select>
24 28
yudao-module-erp/src/test/java/cn/iocoder/yudao/module/erp/service/statistics/ErpStatisticsServiceTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.erp.service.statistics;
  2 +
  3 +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
  4 +import cn.iocoder.yudao.module.erp.dal.mysql.statistics.ErpPurchaseStatisticsMapper;
  5 +import cn.iocoder.yudao.module.erp.dal.mysql.statistics.ErpSaleStatisticsMapper;
  6 +import org.junit.jupiter.api.AfterEach;
  7 +import org.junit.jupiter.api.BeforeEach;
  8 +import org.junit.jupiter.api.Test;
  9 +import org.springframework.boot.test.context.SpringBootTest;
  10 +import org.springframework.boot.test.mock.mockito.MockBean;
  11 +import org.springframework.test.context.ActiveProfiles;
  12 +
  13 +import jakarta.annotation.Resource;
  14 +import java.math.BigDecimal;
  15 +import java.time.LocalDateTime;
  16 +
  17 +import static org.junit.jupiter.api.Assertions.*;
  18 +import static org.mockito.ArgumentMatchers.any;
  19 +import static org.mockito.Mockito.when;
  20 +
  21 +/**
  22 + * ERP 统计服务测试类
  23 + * 主要测试在多租户关闭情况下,统计查询是否能正常工作
  24 + *
  25 + * @author 芋道源码
  26 + */
  27 +@SpringBootTest
  28 +@ActiveProfiles("unit-test")
  29 +public class ErpStatisticsServiceTest {
  30 +
  31 + @Resource
  32 + private ErpSaleStatisticsService saleStatisticsService;
  33 +
  34 + @Resource
  35 + private ErpPurchaseStatisticsService purchaseStatisticsService;
  36 +
  37 + @MockBean
  38 + private ErpSaleStatisticsMapper saleStatisticsMapper;
  39 +
  40 + @MockBean
  41 + private ErpPurchaseStatisticsMapper purchaseStatisticsMapper;
  42 +
  43 + @BeforeEach
  44 + void setUp() {
  45 + // 清理租户上下文
  46 + TenantContextHolder.clear();
  47 + }
  48 +
  49 + @AfterEach
  50 + void tearDown() {
  51 + // 清理租户上下文
  52 + TenantContextHolder.clear();
  53 + }
  54 +
  55 + @Test
  56 + void testSaleStatisticsWithoutTenant() {
  57 + // 准备参数
  58 + LocalDateTime beginTime = LocalDateTime.of(2024, 1, 1, 0, 0, 0);
  59 + LocalDateTime endTime = LocalDateTime.of(2024, 1, 31, 23, 59, 59);
  60 + BigDecimal expectedPrice = new BigDecimal("1000.00");
  61 +
  62 + // Mock 返回值
  63 + when(saleStatisticsMapper.getSalePrice(any(LocalDateTime.class), any(LocalDateTime.class)))
  64 + .thenReturn(expectedPrice);
  65 +
  66 + // 测试:在没有租户ID的情况下调用销售统计
  67 + assertDoesNotThrow(() -> {
  68 + BigDecimal result = saleStatisticsService.getSalePrice(beginTime, endTime);
  69 + assertEquals(expectedPrice, result);
  70 + }, "在多租户关闭时,销售统计查询应该能正常工作");
  71 + }
  72 +
  73 + @Test
  74 + void testPurchaseStatisticsWithoutTenant() {
  75 + // 准备参数
  76 + LocalDateTime beginTime = LocalDateTime.of(2024, 1, 1, 0, 0, 0);
  77 + LocalDateTime endTime = LocalDateTime.of(2024, 1, 31, 23, 59, 59);
  78 + BigDecimal expectedPrice = new BigDecimal("800.00");
  79 +
  80 + // Mock 返回值
  81 + when(purchaseStatisticsMapper.getPurchasePrice(any(LocalDateTime.class), any(LocalDateTime.class)))
  82 + .thenReturn(expectedPrice);
  83 +
  84 + // 测试:在没有租户ID的情况下调用采购统计
  85 + assertDoesNotThrow(() -> {
  86 + BigDecimal result = purchaseStatisticsService.getPurchasePrice(beginTime, endTime);
  87 + assertEquals(expectedPrice, result);
  88 + }, "在多租户关闭时,采购统计查询应该能正常工作");
  89 + }
  90 +
  91 + @Test
  92 + void testSaleStatisticsWithTenant() {
  93 + // 设置租户ID
  94 + Long tenantId = 1L;
  95 + TenantContextHolder.setTenantId(tenantId);
  96 +
  97 + // 准备参数
  98 + LocalDateTime beginTime = LocalDateTime.of(2024, 1, 1, 0, 0, 0);
  99 + LocalDateTime endTime = LocalDateTime.of(2024, 1, 31, 23, 59, 59);
  100 + BigDecimal expectedPrice = new BigDecimal("1500.00");
  101 +
  102 + // Mock 返回值
  103 + when(saleStatisticsMapper.getSalePrice(any(LocalDateTime.class), any(LocalDateTime.class)))
  104 + .thenReturn(expectedPrice);
  105 +
  106 + // 测试:在有租户ID的情况下调用销售统计
  107 + assertDoesNotThrow(() -> {
  108 + BigDecimal result = saleStatisticsService.getSalePrice(beginTime, endTime);
  109 + assertEquals(expectedPrice, result);
  110 + }, "在多租户开启时,销售统计查询应该能正常工作");
  111 +
  112 + // 验证租户ID是否正确设置
  113 + assertEquals(tenantId, TenantContextHolder.getTenantId());
  114 + }
  115 +
  116 + @Test
  117 + void testPurchaseStatisticsWithTenant() {
  118 + // 设置租户ID
  119 + Long tenantId = 2L;
  120 + TenantContextHolder.setTenantId(tenantId);
  121 +
  122 + // 准备参数
  123 + LocalDateTime beginTime = LocalDateTime.of(2024, 1, 1, 0, 0, 0);
  124 + LocalDateTime endTime = LocalDateTime.of(2024, 1, 31, 23, 59, 59);
  125 + BigDecimal expectedPrice = new BigDecimal("1200.00");
  126 +
  127 + // Mock 返回值
  128 + when(purchaseStatisticsMapper.getPurchasePrice(any(LocalDateTime.class), any(LocalDateTime.class)))
  129 + .thenReturn(expectedPrice);
  130 +
  131 + // 测试:在有租户ID的情况下调用采购统计
  132 + assertDoesNotThrow(() -> {
  133 + BigDecimal result = purchaseStatisticsService.getPurchasePrice(beginTime, endTime);
  134 + assertEquals(expectedPrice, result);
  135 + }, "在多租户开启时,采购统计查询应该能正常工作");
  136 +
  137 + // 验证租户ID是否正确设置
  138 + assertEquals(tenantId, TenantContextHolder.getTenantId());
  139 + }
  140 +
  141 + @Test
  142 + void testTenantContextHolderMethods() {
  143 + // 测试 getTenantId() 在没有设置租户时返回 null
  144 + assertNull(TenantContextHolder.getTenantId(), "未设置租户时应该返回 null");
  145 +
  146 + // 设置租户ID
  147 + Long tenantId = 3L;
  148 + TenantContextHolder.setTenantId(tenantId);
  149 + assertEquals(tenantId, TenantContextHolder.getTenantId(), "设置租户后应该能正确获取");
  150 +
  151 + // 清理租户上下文
  152 + TenantContextHolder.clear();
  153 + assertNull(TenantContextHolder.getTenantId(), "清理后应该返回 null");
  154 + }
  155 +}