diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/DataFilterUtil.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/DataFilterUtil.java new file mode 100644 index 00000000..5ea6723f --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/DataFilterUtil.java @@ -0,0 +1,107 @@ +package com.zdxt.enforcementcode.common.util; +import cn.hutool.core.util.StrUtil; +import com.zdxt.enforcementcode.domain.vo.EfcodeVirtualUnitVo; +import com.zdxt.enforcementcode.service.IEfcodeVirtualUnitService; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.dromara.common.core.utils.SpringUtils; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class DataFilterUtil { + + // ======================== 区(ShenZhen_Areas)(2) 的子节点常量 ======================== + + /** 区 */ + public static final String SHENZHEN_AREAS = "ShenZhen_Areas"; + + /** 光明区 */ + public static final String AREA_GUANG_MING_QU = "Area_GuangMingQu"; + + /** 南山区 */ + public static final String AREA_NAN_SHAN_QU = "Area_NanShanQu"; + + /** 坪山区 */ + public static final String AREA_PING_SHAN_QU = "Area_PingsHanQu"; + + /** 大鹏新区 */ + public static final String AREA_DA_PENG_XIN_QU = "Area_DapengXinQu"; + + /** 宝安区 */ + public static final String AREA_BAO_AN_QU = "Area_BaoAnQu"; + + /** 深汕特别合作区 */ + public static final String AREA_SHEN_SHAN_TE_BIE_HE_ZUO_QU = "Area_ShenShanTeBieHeZuoQu"; + + /** 盐田区 */ + public static final String AREA_YAN_TIAN_QU = "Area_YanTianQu"; + + /** 福田区 */ + public static final String AREA_FU_TIAN_QU = "Area_FuTianQu"; + + /** 罗湖区 */ + public static final String AREA_LUO_HU_QU = "Area_LuoHuQu"; + + /** 龙华区 */ + public static final String AREA_LONG_HUA_QU = "Area_LongHuaQu"; + + /** 龙岗区 */ + public static final String AREA_LONG_GANG_QU = "Area_LongGangQu"; + + /** 深圳市直 */ + public static final String ShenZhen_ShiZhi = "ShenZhen_ShiZhi"; + + // ======================== 公开方法 ======================== + + /** + * 获取指定节点下所有子孙级符合条件的部门ID (sys_dept_id) + * + * @param virId 起始节点的虚拟单位ID + * @param includeSearch 是否包含查询节点 (virIncludeSearch = "是") + * @param includeStatistics 是否包含统计节点 (virIncludeStatistics = "是") + * @param includeBusiness 是否包含业务节点 (virShowAtBusiness = "是") + * @return 符合条件的后代节点的 sys_dept_id 列表(已去重,排除空值) + */ + public static List getDescendantDeptIds(String virId, + boolean includeSearch, + boolean includeStatistics, + boolean includeBusiness) { + if (StrUtil.isBlank(virId)) { + return Collections.emptyList(); + } + IEfcodeVirtualUnitService service = SpringUtils.getBean(IEfcodeVirtualUnitService.class); + return service.queryDescendants(virId, includeSearch, includeStatistics, includeBusiness) + .stream() + .map(EfcodeVirtualUnitVo::getSysDeptId) + .filter(StrUtil::isNotBlank) + .distinct() + .collect(Collectors.toList()); + } + + /** + * 获取指定节点下所有子孙级符合条件的虚拟单位ID (vir_id) + * + * @param virId 起始节点的虚拟单位ID + * @param includeSearch 是否包含查询节点 (virIncludeSearch = "是") + * @param includeStatistics 是否包含统计节点 (virIncludeStatistics = "是") + * @param includeBusiness 是否包含业务节点 (virShowAtBusiness = "是") + * @return 符合条件的后代节点的 vir_id 列表(已去重,排除空值) + */ + public static List getDescendantVirIds(String virId, + boolean includeSearch, + boolean includeStatistics, + boolean includeBusiness) { + if (StrUtil.isBlank(virId)) { + return Collections.emptyList(); + } + IEfcodeVirtualUnitService service = SpringUtils.getBean(IEfcodeVirtualUnitService.class); + return service.queryDescendants(virId, includeSearch, includeStatistics, includeBusiness) + .stream() + .map(EfcodeVirtualUnitVo::getVirId) + .filter(StrUtil::isNotBlank) + .distinct() + .collect(Collectors.toList()); + } +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/helper/DataFilterHelper.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/helper/DataFilterHelper.java deleted file mode 100644 index 15b74efb..00000000 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/helper/DataFilterHelper.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.zdxt.enforcementcode.helper; - -public class DataFilterHelper { -} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/mapper/EfcodeVirtualUnitMapper.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/mapper/EfcodeVirtualUnitMapper.java index d0cd6ffd..5efd88b4 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/mapper/EfcodeVirtualUnitMapper.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/mapper/EfcodeVirtualUnitMapper.java @@ -9,6 +9,8 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import java.util.List; + /** * 虚拟单位树Mapper接口 * @@ -30,4 +32,18 @@ public interface EfcodeVirtualUnitMapper extends BaseMapperPlus selectDescendants(@Param("virId") String virId, + @Param("includeSearch") boolean includeSearch, + @Param("includeStatistics") boolean includeStatistics, + @Param("includeBusiness") boolean includeBusiness); } diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEfcodeVirtualUnitService.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEfcodeVirtualUnitService.java index 88bc5ce1..3f13fc64 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEfcodeVirtualUnitService.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEfcodeVirtualUnitService.java @@ -75,6 +75,17 @@ public interface IEfcodeVirtualUnitService { */ List queryTreeList(); + /** + * 查询指定节点的子孙级虚拟单位(按查询/统计/业务条件在SQL层过滤) + * + * @param virId 起始节点的虚拟单位ID + * @param includeSearch 是否包含查询节点 + * @param includeStatistics 是否包含统计节点 + * @param includeBusiness 是否包含业务节点 + * @return 符合条件的后代节点列表 + */ + List queryDescendants(String virId, boolean includeSearch, boolean includeStatistics, boolean includeBusiness); + /** * 查询业务可见的虚拟单位树列表(vir_show_at_business='1') * diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeVirtualUnitServiceImpl.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeVirtualUnitServiceImpl.java index 56bca46b..77adcb97 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeVirtualUnitServiceImpl.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EfcodeVirtualUnitServiceImpl.java @@ -222,7 +222,7 @@ public class EfcodeVirtualUnitServiceImpl implements IEfcodeVirtualUnitService { } /** - * 查询业务可见的虚拟单位树列表(vir_show_at_business='1') + * 查询业务可见的虚拟单位树列表(vir_show_at_business='是') */ @Override public List queryBusinessTreeList() { @@ -233,6 +233,18 @@ public class EfcodeVirtualUnitServiceImpl implements IEfcodeVirtualUnitService { return baseMapper.selectVoList(lqw); } + /** + * 查询指定节点的子孙级虚拟单位(按查询/统计/业务条件在SQL层过滤) + * SQL实现参见 EfcodeVirtualUnitMapper.xml#selectDescendants + */ + @Override + public List queryDescendants(String virId, boolean includeSearch, boolean includeStatistics, boolean includeBusiness) { + if (StringUtils.isBlank(virId)) { + return List.of(); + } + return baseMapper.selectDescendants(virId, includeSearch, includeStatistics, includeBusiness); + } + /** * 根据虚拟单位的sysDeptId分页查询关联人员列表 * SQL实现参见 EfcodeVirtualUnitMapper.xml#selectUnitUsers diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EfcodeVirtualUnitMapper.xml b/zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EfcodeVirtualUnitMapper.xml index 48bc8c5d..e949f0e8 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EfcodeVirtualUnitMapper.xml +++ b/zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EfcodeVirtualUnitMapper.xml @@ -40,4 +40,19 @@ ORDER BY u.dept_id ASC, u.user_id ASC + + diff --git a/zdxt-web-server/zdxt-admin-web-server/src/test/java/org/dromara/test/DataFilterUtilTest.java b/zdxt-web-server/zdxt-admin-web-server/src/test/java/org/dromara/test/DataFilterUtilTest.java new file mode 100644 index 00000000..1293bdb4 --- /dev/null +++ b/zdxt-web-server/zdxt-admin-web-server/src/test/java/org/dromara/test/DataFilterUtilTest.java @@ -0,0 +1,288 @@ +package org.dromara.test; + +import cn.dev33.satoken.context.mock.SaTokenContextMockUtil; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; +import com.zdxt.enforcementcode.common.util.DataFilterUtil; +import org.dromara.ZdxtAdminApplication; +import org.dromara.common.core.domain.model.LoginUser; +import org.dromara.common.satoken.utils.LoginHelper; +import org.junit.jupiter.api.*; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import java.util.HashSet; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * DataFilterUtil 单元测试 + */ +@SpringBootTest(classes = ZdxtAdminApplication.class) +@ActiveProfiles({"dev", "dev-cust-zrb"}) +@AutoConfigureMockMvc +@DisplayName("DataFilterUtil 单元测试") +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class DataFilterUtilTest { + + private static final String TEST_CLIENT_ID = "e5cd7e4891bf95d1d19206ce24a7b32e"; + private static final String TENANT_ID = "000000"; + + @BeforeEach + public void setUp() { + SaTokenContextMockUtil.setMockContext(); + + MockHttpServletRequest mockRequest = new MockHttpServletRequest(); + mockRequest.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"); + RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(mockRequest)); + + LoginUser loginUser = new LoginUser(); + loginUser.setUserId(1L); + loginUser.setUserType("sys_user"); + loginUser.setTenantId(TENANT_ID); + loginUser.setUsername("test_admin"); + loginUser.setNickname("测试管理员"); + loginUser.setDeptId("103"); + loginUser.setDeptName("测试部门"); + loginUser.setRolePermission(new HashSet<>()); + loginUser.setMenuPermission(new HashSet<>()); + + SaLoginParameter param = new SaLoginParameter(); + param.setExtra(LoginHelper.CLIENT_KEY, TEST_CLIENT_ID); + LoginHelper.login(loginUser, param); + } + + @AfterEach + public void tearDown() { + try { + StpUtil.logout(); + } catch (Exception ignored) { + } + RequestContextHolder.resetRequestAttributes(); + SaTokenContextMockUtil.clearContext(); + } + + // ======================== 常量验证 ======================== + + @Test + @Order(1) + @DisplayName("验证区域常量值") + public void testConstants() { + assertEquals("ShenZhen_Areas", DataFilterUtil.SHENZHEN_AREAS); + assertEquals("Area_GuangMingQu", DataFilterUtil.AREA_GUANG_MING_QU); + assertEquals("Area_NanShanQu", DataFilterUtil.AREA_NAN_SHAN_QU); + assertEquals("Area_PingsHanQu", DataFilterUtil.AREA_PING_SHAN_QU); + assertEquals("Area_DapengXinQu", DataFilterUtil.AREA_DA_PENG_XIN_QU); + assertEquals("Area_BaoAnQu", DataFilterUtil.AREA_BAO_AN_QU); + assertEquals("Area_ShenShanTeBieHeZuoQu", DataFilterUtil.AREA_SHEN_SHAN_TE_BIE_HE_ZUO_QU); + assertEquals("Area_YanTianQu", DataFilterUtil.AREA_YAN_TIAN_QU); + assertEquals("Area_FuTianQu", DataFilterUtil.AREA_FU_TIAN_QU); + assertEquals("Area_LuoHuQu", DataFilterUtil.AREA_LUO_HU_QU); + assertEquals("Area_LongHuaQu", DataFilterUtil.AREA_LONG_HUA_QU); + assertEquals("Area_LongGangQu", DataFilterUtil.AREA_LONG_GANG_QU); + assertEquals("ShenZhen_ShiZhi", DataFilterUtil.ShenZhen_ShiZhi); + } + + // ======================== getDescendantDeptIds 测试 ======================== + + @Test + @Order(2) + @DisplayName("getDescendantDeptIds - 空virId返回空列表") + public void testGetDescendantDeptIds_blankVirId() { + List result1 = DataFilterUtil.getDescendantDeptIds(null, true, true, true); + assertNotNull(result1); + assertTrue(result1.isEmpty(), "null virId 应返回空列表"); + + List result2 = DataFilterUtil.getDescendantDeptIds("", true, true, true); + assertNotNull(result2); + assertTrue(result2.isEmpty(), "空字符串 virId 应返回空列表"); + + List result3 = DataFilterUtil.getDescendantDeptIds(" ", true, true, true); + assertNotNull(result3); + assertTrue(result3.isEmpty(), "空白字符串 virId 应返回空列表"); + } + + @Test + @Order(3) + @DisplayName("getDescendantDeptIds - 查询深圳各区子节点的部门ID") + public void testGetDescendantDeptIds_shenzhenAreas() { + List deptIds = DataFilterUtil.getDescendantDeptIds( + DataFilterUtil.SHENZHEN_AREAS, true, true, true); + assertNotNull(deptIds); + System.out.println("SHENZHEN_AREAS 子孙部门ID数量: " + deptIds.size()); + deptIds.forEach(id -> { + assertNotNull(id); + assertFalse(id.isBlank(), "部门ID不应为空白字符串"); + }); + assertFalse(deptIds.isEmpty(), "深圳各区下应存在子孙部门"); + assertEquals(deptIds.size(), deptIds.stream().distinct().count(), "部门ID不应有重复"); + } + + @Test + @Order(4) + @DisplayName("getDescendantDeptIds - 查询光明区子节点的部门ID") + public void testGetDescendantDeptIds_guangMingQu() { + List deptIds = DataFilterUtil.getDescendantDeptIds( + DataFilterUtil.AREA_GUANG_MING_QU, true, true, true); + assertNotNull(deptIds); + System.out.println("光明区子孙部门ID数量: " + deptIds.size()); + deptIds.forEach(id -> assertFalse(id.isBlank(), "部门ID不应为空白")); + } + + @Test + @Order(5) + @DisplayName("getDescendantDeptIds - 查询市直子节点的部门ID") + public void testGetDescendantDeptIds_shiZhi() { + List deptIds = DataFilterUtil.getDescendantDeptIds( + DataFilterUtil.ShenZhen_ShiZhi, true, true, true); + assertNotNull(deptIds); + System.out.println("市直子孙部门ID数量: " + deptIds.size()); + deptIds.forEach(id -> assertFalse(id.isBlank(), "部门ID不应为空白")); + } + + @Test + @Order(6) + @DisplayName("getDescendantDeptIds - 不存在的virId返回空列表") + public void testGetDescendantDeptIds_nonExistentVirId() { + List deptIds = DataFilterUtil.getDescendantDeptIds( + "NON_EXISTENT_VIR_ID_12345", true, true, true); + assertNotNull(deptIds); + assertTrue(deptIds.isEmpty(), "不存在的virId应返回空列表"); + } + + @Test + @Order(7) + @DisplayName("getDescendantDeptIds - 不同过滤条件组合测试") + public void testGetDescendantDeptIds_filterCombinations() { + String virId = DataFilterUtil.SHENZHEN_AREAS; + + List allTrue = DataFilterUtil.getDescendantDeptIds(virId, true, true, true); + List noSearch = DataFilterUtil.getDescendantDeptIds(virId, false, true, true); + List noStatistics = DataFilterUtil.getDescendantDeptIds(virId, true, false, true); + List noBusiness = DataFilterUtil.getDescendantDeptIds(virId, true, true, false); + List allFalse = DataFilterUtil.getDescendantDeptIds(virId, false, false, false); + + assertNotNull(allTrue); + assertNotNull(noSearch); + assertNotNull(noStatistics); + assertNotNull(noBusiness); + assertNotNull(allFalse); + + System.out.println("全部包含: " + allTrue.size()); + System.out.println("不包含查询节点: " + noSearch.size()); + System.out.println("不包含统计节点: " + noStatistics.size()); + System.out.println("不包含业务节点: " + noBusiness.size()); + System.out.println("全部不包含: " + allFalse.size()); + } + + // ======================== getDescendantVirIds 测试 ======================== + + @Test + @Order(8) + @DisplayName("getDescendantVirIds - 空virId返回空列表") + public void testGetDescendantVirIds_blankVirId() { + List result1 = DataFilterUtil.getDescendantVirIds(null, true, true, true); + assertNotNull(result1); + assertTrue(result1.isEmpty(), "null virId 应返回空列表"); + + List result2 = DataFilterUtil.getDescendantVirIds("", true, true, true); + assertNotNull(result2); + assertTrue(result2.isEmpty(), "空字符串 virId 应返回空列表"); + + List result3 = DataFilterUtil.getDescendantVirIds(" ", true, true, true); + assertNotNull(result3); + assertTrue(result3.isEmpty(), "空白字符串 virId 应返回空列表"); + } + + @Test + @Order(9) + @DisplayName("getDescendantVirIds - 查询深圳各区子节点的虚拟单位ID") + public void testGetDescendantVirIds_shenzhenAreas() { + List virIds = DataFilterUtil.getDescendantVirIds( + DataFilterUtil.SHENZHEN_AREAS, true, true, true); + assertNotNull(virIds); + System.out.println("SHENZHEN_AREAS 子孙虚拟单位ID数量: " + virIds.size()); + virIds.forEach(id -> { + assertNotNull(id); + assertFalse(id.isBlank(), "虚拟单位ID不应为空白字符串"); + }); + assertFalse(virIds.isEmpty(), "深圳各区下应存在子孙虚拟单位"); + assertEquals(virIds.size(), virIds.stream().distinct().count(), "虚拟单位ID不应有重复"); + } + + @Test + @Order(10) + @DisplayName("getDescendantVirIds - 查询市直子节点的虚拟单位ID") + public void testGetDescendantVirIds_shiZhi() { + List virIds = DataFilterUtil.getDescendantVirIds( + DataFilterUtil.ShenZhen_ShiZhi, true, true, true); + assertNotNull(virIds); + System.out.println("市直子孙虚拟单位ID数量: " + virIds.size()); + virIds.forEach(id -> assertFalse(id.isBlank(), "虚拟单位ID不应为空白")); + } + + @Test + @Order(11) + @DisplayName("getDescendantVirIds - 不存在的virId返回空列表") + public void testGetDescendantVirIds_nonExistentVirId() { + List virIds = DataFilterUtil.getDescendantVirIds( + "NON_EXISTENT_VIR_ID_12345", true, true, true); + assertNotNull(virIds); + assertTrue(virIds.isEmpty(), "不存在的virId应返回空列表"); + } + + @Test + @Order(12) + @DisplayName("getDescendantDeptIds - 遍历所有区域常量查询子孙部门") + public void testGetDescendantDeptIds_allAreaConstants() { + String[] areaConstants = { + DataFilterUtil.AREA_GUANG_MING_QU, + DataFilterUtil.AREA_NAN_SHAN_QU, + DataFilterUtil.AREA_PING_SHAN_QU, + DataFilterUtil.AREA_DA_PENG_XIN_QU, + DataFilterUtil.AREA_BAO_AN_QU, + DataFilterUtil.AREA_SHEN_SHAN_TE_BIE_HE_ZUO_QU, + DataFilterUtil.AREA_YAN_TIAN_QU, + DataFilterUtil.AREA_FU_TIAN_QU, + DataFilterUtil.AREA_LUO_HU_QU, + DataFilterUtil.AREA_LONG_HUA_QU, + DataFilterUtil.AREA_LONG_GANG_QU, + }; + + for (String area : areaConstants) { + List deptIds = DataFilterUtil.getDescendantDeptIds(area, true, true, true); + assertNotNull(deptIds, area + " 查询不应返回null"); + System.out.println(area + " 子孙部门ID数量: " + deptIds.size()); + } + } + + @Test + @Order(13) + @DisplayName("getDescendantVirIds - 不同过滤条件组合测试") + public void testGetDescendantVirIds_filterCombinations() { + String virId = DataFilterUtil.SHENZHEN_AREAS; + + List allTrue = DataFilterUtil.getDescendantVirIds(virId, true, true, true); + List noSearch = DataFilterUtil.getDescendantVirIds(virId, false, true, true); + List noStatistics = DataFilterUtil.getDescendantVirIds(virId, true, false, true); + List noBusiness = DataFilterUtil.getDescendantVirIds(virId, true, true, false); + List allFalse = DataFilterUtil.getDescendantVirIds(virId, false, false, false); + + assertNotNull(allTrue); + assertNotNull(noSearch); + assertNotNull(noStatistics); + assertNotNull(noBusiness); + assertNotNull(allFalse); + + System.out.println("VirIds 全部包含: " + allTrue.size()); + System.out.println("VirIds 不包含查询节点: " + noSearch.size()); + System.out.println("VirIds 不包含统计节点: " + noStatistics.size()); + System.out.println("VirIds 不包含业务节点: " + noBusiness.size()); + System.out.println("VirIds 全部不包含: " + allFalse.size()); + } +}