diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/BureauUnitController.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/BureauUnitController.java index 784ba8eb..54286bc5 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/BureauUnitController.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/BureauUnitController.java @@ -19,7 +19,6 @@ import org.springframework.web.bind.annotation.RestController; public class BureauUnitController { private final IEfcodeVirtualUnitService efcodeVirtualUnitService; - @SaCheckPermission("enforcementcode:virtualUnit:list") @GetMapping("/users") public TableDataInfo queryUsers( @RequestParam(required = false) String sysDeptId, diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/EnterpriseSearchController.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/EnterpriseSearchController.java index 04bc5f0b..c10a7f8c 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/EnterpriseSearchController.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/EnterpriseSearchController.java @@ -1,4 +1,86 @@ package com.zdxt.enforcementcode.controller; -public class EnterpriseSearchController { +import com.zdxt.enforcementcode.domain.vo.EnterpriseSearchVo; +import com.zdxt.enforcementcode.service.IEnterpriseInformationService; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.web.core.BaseController; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 企业搜索 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/enforcementcode/enterpriseSearch") +public class EnterpriseSearchController extends BaseController { + + private final IEnterpriseInformationService enterpriseInformationService; + + /** + * 根据企业名称模糊搜索 + * + * @param enterpriseName 企业名称关键字(>=3个字符) + */ + @GetMapping("/searchUnionByEnterpriseName") + public R> searchUnionByEnterpriseName(@RequestParam String enterpriseName) { + return R.ok(enterpriseInformationService.searchUnionByEnterpriseName(enterpriseName)); + } + + /** + * 根据企业信用代码查询 + * + * @param enterpriseNumber 企业统一社会信用代码 / 纳税识别号 + */ + @GetMapping("/searchUnionByEnterpriseNumber") + public R> searchUnionByEnterpriseNumber(@RequestParam String enterpriseNumber) { + return R.ok(enterpriseInformationService.searchUnionByEnterpriseNumber(enterpriseNumber)); + } + + /** + * 仅在 enterprise_information 表中按企业名称模糊搜索 + * + * @param enterpriseName 企业名称关键字(>=3个字符) + */ + @GetMapping("/searchEnterpriseInfoByName") + public R> searchEnterpriseInfoByName(@RequestParam String enterpriseName) { + return R.ok(enterpriseInformationService.searchEnterpriseInfoByName(enterpriseName)); + } + + /** + * 仅在 sszt_jbxx110497_new 表中按企业名称前缀搜索 + * + * @param enterpriseName 企业名称关键字(>=3个字符) + */ + @GetMapping("/searchSsztByName") + public R> searchSsztByName(@RequestParam String enterpriseName) { + return R.ok(enterpriseInformationService.searchSsztByName(enterpriseName)); + } + + /** + * 仅在 enterprise_information 表中按企业信用代码前缀查询 + * + * @param enterpriseNumber 企业信用代码 + */ + @GetMapping("/searchEnterpriseInfoByNumber") + public R> searchEnterpriseInfoByNumber(@RequestParam String enterpriseNumber) { + return R.ok(enterpriseInformationService.searchEnterpriseInfoByNumber(enterpriseNumber)); + } + + /** + * 仅在 sszt_jbxx110497_new 表中按企业信用代码前缀查询 + * + * @param enterpriseNumber 企业信用代码 + */ + @GetMapping("/searchSsztByNumber") + public R> searchSsztByNumber(@RequestParam String enterpriseNumber) { + return R.ok(enterpriseInformationService.searchSsztByNumber(enterpriseNumber)); + } } diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/EnterpriseSearchVo.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/EnterpriseSearchVo.java new file mode 100644 index 00000000..aa9a8605 --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/domain/vo/EnterpriseSearchVo.java @@ -0,0 +1,26 @@ +package com.zdxt.enforcementcode.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 企业名称搜索结果视图对象 + */ +@Data +public class EnterpriseSearchVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 企业纳税识别号 + */ + private String enterpriseNumber; + + /** + * 企业名称 + */ + private String enterpriseName; +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/mapper/EnterpriseInformationMapper.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/mapper/EnterpriseInformationMapper.java index 9b03905f..dccba19e 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/mapper/EnterpriseInformationMapper.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/mapper/EnterpriseInformationMapper.java @@ -2,9 +2,13 @@ package com.zdxt.enforcementcode.mapper; import com.zdxt.enforcementcode.domain.EnterpriseInformation; import com.zdxt.enforcementcode.domain.vo.EnterpriseInformationVo; +import com.zdxt.enforcementcode.domain.vo.EnterpriseSearchVo; 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接口 * @@ -14,4 +18,51 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; @Mapper public interface EnterpriseInformationMapper extends BaseMapperPlus { + /** + * 根据企业名称关键字联合搜索(enterprise_information + sszt_jbxx110497_new) + * + * @param keyword 去除深圳/深圳市后的关键字 + * @return 前20条搜索结果 + */ + List searchUnionByEnterpriseName(@Param("keyword") String keyword); + + /** + * 根据企业统一社会信用代码联合查询(enterprise_information + sszt_jbxx110497_new) + * + * @param enterpriseNumber 企业信用代码(统一社会信用代码 / 纳税识别号) + * @return 查询结果 + */ + List searchUnionByEnterpriseNumber(@Param("enterpriseNumber") String enterpriseNumber); + + /** + * 仅在 enterprise_information 表中按企业名称模糊搜索 + * + * @param keyword 去除深圳/深圳市后的关键字 + * @return 前20条搜索结果 + */ + List searchEnterpriseInfoByName(@Param("keyword") String keyword); + + /** + * 仅在 sszt_jbxx110497_new 表中按企业名称前缀搜索(深圳/深圳市 前缀) + * + * @param keyword 去除深圳/深圳市后的关键字 + * @return 前20条搜索结果 + */ + List searchSsztByName(@Param("keyword") String keyword); + + /** + * 仅在 enterprise_information 表中按企业信用代码前缀查询 + * + * @param enterpriseNumber 企业信用代码 + * @return 前20条搜索结果 + */ + List searchEnterpriseInfoByNumber(@Param("enterpriseNumber") String enterpriseNumber); + + /** + * 仅在 sszt_jbxx110497_new 表中按企业信用代码前缀查询 + * + * @param enterpriseNumber 企业信用代码 + * @return 前20条搜索结果 + */ + List searchSsztByNumber(@Param("enterpriseNumber") String enterpriseNumber); } diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEnterpriseInformationService.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEnterpriseInformationService.java index 2a3cfb58..4e107f87 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEnterpriseInformationService.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/IEnterpriseInformationService.java @@ -2,6 +2,7 @@ package com.zdxt.enforcementcode.service; import com.zdxt.enforcementcode.domain.bo.EnterpriseInformationBo; import com.zdxt.enforcementcode.domain.vo.EnterpriseInformationVo; +import com.zdxt.enforcementcode.domain.vo.EnterpriseSearchVo; import com.zdxt.enforcementcode.domain.vo.QrCodeGenerateHistoryVo; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.PageQuery; @@ -106,4 +107,55 @@ public interface IEnterpriseInformationService { * @return 二维码生成历史记录 */ QrCodeGenerateHistoryVo forceGenerateQrCode(String type, String enterpriseNumber, String formModule, String reasonsChange); + + /** + * 根据企业名称模糊搜索(联合 enterprise_information 和 sszt_jbxx110497_new 两张表) + *

自动去除"深圳市"、"深圳"前缀,字数>=3时才执行搜索,返回前20条

+ * + * @param enterpriseName 企业名称关键字 + * @return 搜索结果列表 + */ + List searchUnionByEnterpriseName(String enterpriseName); + + /** + * 根据企业统一社会信用代码联合查询(enterprise_information + sszt_jbxx110497_new) + * + * @param enterpriseNumber 企业信用代码 + * @return 搜索结果列表 + */ + List searchUnionByEnterpriseNumber(String enterpriseNumber); + + /** + * 仅在 enterprise_information 表中按企业名称模糊搜索 + *

自动去除"深圳市"、"深圳"前缀,字数>=3时才执行搜索

+ * + * @param enterpriseName 企业名称关键字 + * @return 搜索结果列表 + */ + List searchEnterpriseInfoByName(String enterpriseName); + + /** + * 仅在 sszt_jbxx110497_new 表中按企业名称前缀搜索 + *

自动去除"深圳市"、"深圳"前缀,字数>=3时才执行搜索

+ * + * @param enterpriseName 企业名称关键字 + * @return 搜索结果列表 + */ + List searchSsztByName(String enterpriseName); + + /** + * 仅在 enterprise_information 表中按企业信用代码前缀查询 + * + * @param enterpriseNumber 企业信用代码 + * @return 搜索结果列表 + */ + List searchEnterpriseInfoByNumber(String enterpriseNumber); + + /** + * 仅在 sszt_jbxx110497_new 表中按企业信用代码前缀查询 + * + * @param enterpriseNumber 企业信用代码 + * @return 搜索结果列表 + */ + List searchSsztByNumber(String enterpriseNumber); } diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EnterpriseInformationServiceImpl.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EnterpriseInformationServiceImpl.java index c63389a0..3206cedc 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EnterpriseInformationServiceImpl.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/service/impl/EnterpriseInformationServiceImpl.java @@ -4,6 +4,7 @@ import com.zdxt.enforcementcode.domain.EnterpriseInformation; import com.zdxt.enforcementcode.domain.bo.EnterpriseInformationBo; import com.zdxt.enforcementcode.domain.bo.QrCodeGenerateHistoryBo; import com.zdxt.enforcementcode.domain.vo.EnterpriseInformationVo; +import com.zdxt.enforcementcode.domain.vo.EnterpriseSearchVo; import com.zdxt.enforcementcode.domain.vo.QrCodeGenerateHistoryVo; import com.zdxt.enforcementcode.mapper.EnterpriseInformationMapper; import com.zdxt.enforcementcode.service.IEnterpriseInformationService; @@ -265,4 +266,93 @@ public class EnterpriseInformationServiceImpl implements IEnterpriseInformationS return qrCodeVo; } + + /** + * 根据企业名称模糊搜索 + *

自动去除"深圳市"、"深圳"前缀,字数>=3时才执行搜索

+ */ + @Override + public List searchUnionByEnterpriseName(String enterpriseName) { + if (StringUtils.isBlank(enterpriseName)) { + return List.of(); + } + // 去除"深圳市"和"深圳"前缀(先去"深圳市"避免残留"市"字) + String keyword = enterpriseName.trim() + .replace("深圳市", "") + .replace("深圳", ""); + if (keyword.length() < 3) { + return List.of(); + } + return baseMapper.searchUnionByEnterpriseName(keyword); + } + + /** + * 根据企业统一社会信用代码联合查询 + */ + @Override + public List searchUnionByEnterpriseNumber(String creditCode) { + if (StringUtils.isBlank(creditCode)) { + return List.of(); + } + return baseMapper.searchUnionByEnterpriseNumber(creditCode.trim()); + } + + /** + * 仅在 enterprise_information 表中按企业名称模糊搜索 + */ + @Override + public List searchEnterpriseInfoByName(String enterpriseName) { + String keyword = normalizeNameKeyword(enterpriseName); + if (keyword == null) { + return List.of(); + } + return baseMapper.searchEnterpriseInfoByName(keyword); + } + + /** + * 仅在 sszt_jbxx110497_new 表中按企业名称前缀搜索 + */ + @Override + public List searchSsztByName(String enterpriseName) { + String keyword = normalizeNameKeyword(enterpriseName); + if (keyword == null) { + return List.of(); + } + return baseMapper.searchSsztByName(keyword); + } + + /** + * 仅在 enterprise_information 表中按企业信用代码前缀查询 + */ + @Override + public List searchEnterpriseInfoByNumber(String enterpriseNumber) { + if (StringUtils.isBlank(enterpriseNumber)) { + return List.of(); + } + return baseMapper.searchEnterpriseInfoByNumber(enterpriseNumber.trim()); + } + + /** + * 仅在 sszt_jbxx110497_new 表中按企业信用代码前缀查询 + */ + @Override + public List searchSsztByNumber(String enterpriseNumber) { + if (StringUtils.isBlank(enterpriseNumber)) { + return List.of(); + } + return baseMapper.searchSsztByNumber(enterpriseNumber.trim()); + } + + /** + * 统一处理企业名称关键字:去除"深圳市"/"深圳"前缀,长度<3返回 null + */ + private String normalizeNameKeyword(String enterpriseName) { + if (StringUtils.isBlank(enterpriseName)) { + return null; + } + String keyword = enterpriseName.trim() + .replace("深圳市", "") + .replace("深圳", ""); + return keyword.length() < 3 ? null : keyword; + } } diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EnterpriseInformationMapper.xml b/zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EnterpriseInformationMapper.xml index ddeacd3a..9f4226ec 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EnterpriseInformationMapper.xml +++ b/zdxt-modules/zdxt-enforcement-code/src/main/resources/mapper/enforcementcode/EnterpriseInformationMapper.xml @@ -3,4 +3,65 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> + + + + + + + + + + + + + diff --git a/zdxt-web-server/zdxt-admin-web-server/src/test/java/com/zdxt/rest/test/EnterpriseSearchControllerTest.java b/zdxt-web-server/zdxt-admin-web-server/src/test/java/com/zdxt/rest/test/EnterpriseSearchControllerTest.java new file mode 100644 index 00000000..e2d3c46e --- /dev/null +++ b/zdxt-web-server/zdxt-admin-web-server/src/test/java/com/zdxt/rest/test/EnterpriseSearchControllerTest.java @@ -0,0 +1,542 @@ +package com.zdxt.rest.test; + +import cn.dev33.satoken.context.mock.SaTokenContextMockUtil; +import cn.dev33.satoken.stp.parameter.SaLoginParameter; +import cn.dev33.satoken.stp.StpUtil; +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.mock.web.MockHttpServletRequest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; + +import java.nio.charset.StandardCharsets; +import java.util.HashSet; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +/** + * EnterpriseSearchController 企业搜索接口测试 + *

+ * 覆盖以下接口: + * 1. GET /enforcementcode/enterpriseSearch/searchByName - 按名称联合搜索(UNION) + * 2. GET /enforcementcode/enterpriseSearch/searchUnionByEnterpriseNumber - 按信用代码联合搜索(UNION) + * 3. GET /enforcementcode/enterpriseSearch/searchEnterpriseInfoByName - 仅在 enterprise_information 按名称搜索 + * 4. GET /enforcementcode/enterpriseSearch/searchSsztByName - 仅在 sszt_jbxx110497_new 按名称搜索 + * 5. GET /enforcementcode/enterpriseSearch/searchEnterpriseInfoByNumber - 仅在 enterprise_information 按信用代码搜索 + * 6. GET /enforcementcode/enterpriseSearch/searchSsztByNumber - 仅在 sszt_jbxx110497_new 按信用代码搜索 + *

+ * + * @author zdxt + */ +@SpringBootTest(classes = ZdxtAdminApplication.class) +@AutoConfigureMockMvc +@ActiveProfiles({"dev", "dev-cust-zrb"}) +@DisplayName("EnterpriseSearchController 企业搜索接口测试") +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class EnterpriseSearchControllerTest { + + @Autowired + private MockMvc mockMvc; + + private static final String BASE_URL = "/enforcementcode/enterpriseSearch"; + private static final String TEST_CLIENT_ID = "e5cd7e4891bf95d1d19206ce24a7b32e"; + /** 测试用用户ID */ + private static final Long TEST_USER_ID = 1L; + + /** 登录后获取的 token */ + private String token; + + @BeforeEach + public void setUp() { + // 初始化 Sa-Token 上下文 + 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(TEST_USER_ID); + loginUser.setUserType("sys_user"); + loginUser.setTenantId("000000"); + loginUser.setUsername("test_admin"); + loginUser.setNickname("测试管理员"); + loginUser.setDeptId("103"); + loginUser.setDeptName("测试部门"); + loginUser.setRolePermission(new HashSet<>()); + loginUser.setMenuPermission(new HashSet<>()); + + // 模拟登录获取 token + SaLoginParameter param = new SaLoginParameter(); + param.setExtra(LoginHelper.CLIENT_KEY, TEST_CLIENT_ID); + LoginHelper.login(loginUser, param); + token = StpUtil.getTokenValue(); + } + + @AfterEach + public void tearDown() { + try { + StpUtil.logout(); + } catch (Exception ignored) { + } + RequestContextHolder.resetRequestAttributes(); + SaTokenContextMockUtil.clearContext(); + } + + // ==================== /searchByName 联合搜索(按名称) ==================== + + @DisplayName("UNION-按名称 - 正常请求(>=3字符)") + @Test + @Order(1) + public void testSearchByName() throws Exception { + MvcResult result = mockMvc.perform(get(BASE_URL + "/searchUnionByEnterpriseName") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID) + .param("enterpriseName", "鑫蓝光纸业")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.data").isArray()) + .andReturn(); + + String body = result.getResponse().getContentAsString(StandardCharsets.UTF_8); + System.out.println("UNION-按名称接口响应: " + body); + } + + @DisplayName("UNION-按名称 - 自动去除'深圳市'前缀") + @Test + @Order(1) + public void testSearchByNameStripShenzhenShi() throws Exception { + mockMvc.perform(get(BASE_URL + "/searchUnionByEnterpriseName") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID) + .param("enterpriseName", "深圳市强达电路")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.data").isArray()); + } + + @DisplayName("UNION-按名称 - 自动去除'深圳'前缀") + @Test + @Order(1) + public void testSearchByNameStripShenzhen() throws Exception { + mockMvc.perform(get(BASE_URL + "/searchUnionByEnterpriseName") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID) + .param("enterpriseName", "深圳康泰生物")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.data").isArray()); + } + + @DisplayName("UNION-按名称 - 关键字过短返回空列表") + @Test + @Order(1) + public void testSearchByNameTooShort() throws Exception { + mockMvc.perform(get(BASE_URL + "/searchUnionByEnterpriseName") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID) + .param("enterpriseName", "深圳市AB")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.data.length()").value(0)); + } + + @DisplayName("UNION-按名称 - 缺少参数返回 400") + @Test + @Order(1) + public void testSearchByNameMissingParam() throws Exception { + mockMvc.perform(get(BASE_URL + "/searchUnionByEnterpriseName") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID)) + .andDo(print()) + .andExpect(status().isBadRequest()); + } + + @DisplayName("UNION-按名称 - 不存在关键字返回空列表") + @Test + @Order(1) + public void testSearchByNameNonExist() throws Exception { + mockMvc.perform(get(BASE_URL + "/searchUnionByEnterpriseName") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID) + .param("enterpriseName", "不存在的企业关键字XYZ123")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.data.length()").value(0)); + } + + @DisplayName("UNION-按名称 - LIMIT 20 验证") + @Test + @Order(1) + public void testSearchByNameLimit20() throws Exception { + MvcResult result = mockMvc.perform(get(BASE_URL + "/searchUnionByEnterpriseName") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID) + .param("enterpriseName", "有限公司")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andReturn(); + + int count = countOccurrences(result.getResponse().getContentAsString(StandardCharsets.UTF_8), + "\"enterpriseNumber\""); + Assertions.assertTrue(count <= 20, "LIMIT 20 校验失败,count=" + count); + System.out.println("UNION-按名称返回条数:" + count); + } + + // ==================== /searchUnionByEnterpriseNumber 联合搜索(按信用代码) ==================== + + @DisplayName("UNION-按信用代码 - 正常请求") + @Test + @Order(2) + public void testSearchUnionByEnterpriseNumber() throws Exception { + MvcResult result = mockMvc.perform(get(BASE_URL + "/searchUnionByEnterpriseNumber") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID) + .param("enterpriseNumber", "91440300")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.data").isArray()) + .andReturn(); + + System.out.println("UNION-按信用代码接口响应: " + + result.getResponse().getContentAsString(StandardCharsets.UTF_8)); + } + + @DisplayName("UNION-按信用代码 - 短前缀匹配多条但≤20") + @Test + @Order(2) + public void testSearchUnionByEnterpriseNumberPrefix() throws Exception { + MvcResult result = mockMvc.perform(get(BASE_URL + "/searchUnionByEnterpriseNumber") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID) + .param("enterpriseNumber", "9144")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andReturn(); + + int count = countOccurrences(result.getResponse().getContentAsString(StandardCharsets.UTF_8), + "\"enterpriseNumber\""); + Assertions.assertTrue(count <= 20, "LIMIT 20 校验失败,count=" + count); + } + + @DisplayName("UNION-按信用代码 - 完整代码") + @Test + @Order(2) + public void testSearchUnionByEnterpriseNumberFullCode() throws Exception { + mockMvc.perform(get(BASE_URL + "/searchUnionByEnterpriseNumber") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID) + .param("enterpriseNumber", "91440300788302281W")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.data").isArray()); + } + + @DisplayName("UNION-按信用代码 - 缺少参数返回 400") + @Test + @Order(2) + public void testSearchUnionByEnterpriseNumberMissingParam() throws Exception { + mockMvc.perform(get(BASE_URL + "/searchUnionByEnterpriseNumber") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID)) + .andDo(print()) + .andExpect(status().isBadRequest()); + } + + @DisplayName("UNION-按信用代码 - 不存在代码返回空") + @Test + @Order(2) + public void testSearchUnionByEnterpriseNumberNonExist() throws Exception { + mockMvc.perform(get(BASE_URL + "/searchUnionByEnterpriseNumber") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID) + .param("enterpriseNumber", "ZZZZZZZZZZZZZZZZZZ")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.data.length()").value(0)); + } + + // ==================== /searchEnterpriseInfoByName 单表-按名称 ==================== + + @DisplayName("单表 enterprise_information 按名称 - 正常请求") + @Test + @Order(3) + public void testSearchEnterpriseInfoByName() throws Exception { + MvcResult result = mockMvc.perform(get(BASE_URL + "/searchEnterpriseInfoByName") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID) + .param("enterpriseName", "鑫蓝光纸业")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.data").isArray()) + .andReturn(); + + System.out.println("单表 enterprise_information 按名称响应: " + + result.getResponse().getContentAsString(StandardCharsets.UTF_8)); + } + + @DisplayName("单表 enterprise_information 按名称 - 去前缀后过短返回空") + @Test + @Order(3) + public void testSearchEnterpriseInfoByNameTooShort() throws Exception { + mockMvc.perform(get(BASE_URL + "/searchEnterpriseInfoByName") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID) + .param("enterpriseName", "深圳市A")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.length()").value(0)); + } + + @DisplayName("单表 enterprise_information 按名称 - 缺少参数返回 400") + @Test + @Order(3) + public void testSearchEnterpriseInfoByNameMissingParam() throws Exception { + mockMvc.perform(get(BASE_URL + "/searchEnterpriseInfoByName") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID)) + .andDo(print()) + .andExpect(status().isBadRequest()); + } + + @DisplayName("单表 enterprise_information 按名称 - LIMIT 20") + @Test + @Order(3) + public void testSearchEnterpriseInfoByNameLimit20() throws Exception { + MvcResult result = mockMvc.perform(get(BASE_URL + "/searchEnterpriseInfoByName") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID) + .param("enterpriseName", "有限公司")) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + + int count = countOccurrences(result.getResponse().getContentAsString(StandardCharsets.UTF_8), + "\"enterpriseNumber\""); + Assertions.assertTrue(count <= 20, "LIMIT 20 校验失败,count=" + count); + } + + // ==================== /searchSsztByName 单表 sszt 按名称 ==================== + + @DisplayName("单表 sszt 按名称 - 正常请求(带'深圳市'前缀)") + @Test + @Order(4) + public void testSearchSsztByName() throws Exception { + MvcResult result = mockMvc.perform(get(BASE_URL + "/searchSsztByName") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID) + .param("enterpriseName", "深圳市强达电路")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.data").isArray()) + .andReturn(); + + System.out.println("单表 sszt 按名称响应: " + + result.getResponse().getContentAsString(StandardCharsets.UTF_8)); + } + + @DisplayName("单表 sszt 按名称 - 去前缀后过短返回空") + @Test + @Order(4) + public void testSearchSsztByNameTooShort() throws Exception { + mockMvc.perform(get(BASE_URL + "/searchSsztByName") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID) + .param("enterpriseName", "深圳")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.length()").value(0)); + } + + @DisplayName("单表 sszt 按名称 - 缺少参数返回 400") + @Test + @Order(4) + public void testSearchSsztByNameMissingParam() throws Exception { + mockMvc.perform(get(BASE_URL + "/searchSsztByName") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID)) + .andDo(print()) + .andExpect(status().isBadRequest()); + } + + @DisplayName("单表 sszt 按名称 - LIMIT 20") + @Test + @Order(4) + public void testSearchSsztByNameLimit20() throws Exception { + MvcResult result = mockMvc.perform(get(BASE_URL + "/searchSsztByName") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID) + .param("enterpriseName", "有限公司")) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + + int count = countOccurrences(result.getResponse().getContentAsString(StandardCharsets.UTF_8), + "\"enterpriseNumber\""); + Assertions.assertTrue(count <= 20, "LIMIT 20 校验失败,count=" + count); + } + + // ==================== /searchEnterpriseInfoByNumber 单表 enterprise_info 按信用代码 ==================== + + @DisplayName("单表 enterprise_information 按信用代码 - 正常前缀") + @Test + @Order(5) + public void testSearchEnterpriseInfoByNumber() throws Exception { + MvcResult result = mockMvc.perform(get(BASE_URL + "/searchEnterpriseInfoByNumber") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID) + .param("enterpriseNumber", "91440300")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.data").isArray()) + .andReturn(); + + System.out.println("单表 enterprise_info 按信用代码响应: " + + result.getResponse().getContentAsString(StandardCharsets.UTF_8)); + } + + @DisplayName("单表 enterprise_information 按信用代码 - 缺少参数返回 400") + @Test + @Order(5) + public void testSearchEnterpriseInfoByNumberMissingParam() throws Exception { + mockMvc.perform(get(BASE_URL + "/searchEnterpriseInfoByNumber") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID)) + .andDo(print()) + .andExpect(status().isBadRequest()); + } + + @DisplayName("单表 enterprise_information 按信用代码 - 不存在代码返回空") + @Test + @Order(5) + public void testSearchEnterpriseInfoByNumberNonExist() throws Exception { + mockMvc.perform(get(BASE_URL + "/searchEnterpriseInfoByNumber") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID) + .param("enterpriseNumber", "ZZZZZZZZZZZZZZZZZZ")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.length()").value(0)); + } + + @DisplayName("单表 enterprise_information 按信用代码 - LIMIT 20") + @Test + @Order(5) + public void testSearchEnterpriseInfoByNumberLimit20() throws Exception { + MvcResult result = mockMvc.perform(get(BASE_URL + "/searchEnterpriseInfoByNumber") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID) + .param("enterpriseNumber", "9144")) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + + int count = countOccurrences(result.getResponse().getContentAsString(StandardCharsets.UTF_8), + "\"enterpriseNumber\""); + Assertions.assertTrue(count <= 20, "LIMIT 20 校验失败,count=" + count); + } + + // ==================== /searchSsztByNumber 单表 sszt 按信用代码 ==================== + + @DisplayName("单表 sszt 按信用代码 - 正常前缀") + @Test + @Order(6) + public void testSearchSsztByNumber() throws Exception { + MvcResult result = mockMvc.perform(get(BASE_URL + "/searchSsztByNumber") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID) + .param("enterpriseNumber", "91440300")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.data").isArray()) + .andReturn(); + + System.out.println("单表 sszt 按信用代码响应: " + + result.getResponse().getContentAsString(StandardCharsets.UTF_8)); + } + + @DisplayName("单表 sszt 按信用代码 - 缺少参数返回 400") + @Test + @Order(6) + public void testSearchSsztByNumberMissingParam() throws Exception { + mockMvc.perform(get(BASE_URL + "/searchSsztByNumber") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID)) + .andDo(print()) + .andExpect(status().isBadRequest()); + } + + @DisplayName("单表 sszt 按信用代码 - 不存在代码返回空") + @Test + @Order(6) + public void testSearchSsztByNumberNonExist() throws Exception { + mockMvc.perform(get(BASE_URL + "/searchSsztByNumber") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID) + .param("enterpriseNumber", "ZZZZZZZZZZZZZZZZZZ")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.length()").value(0)); + } + + @DisplayName("单表 sszt 按信用代码 - LIMIT 20") + @Test + @Order(6) + public void testSearchSsztByNumberLimit20() throws Exception { + MvcResult result = mockMvc.perform(get(BASE_URL + "/searchSsztByNumber") + .header("Authorization", "Bearer " + token) + .header("clientid", TEST_CLIENT_ID) + .param("enterpriseNumber", "9144")) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + + int count = countOccurrences(result.getResponse().getContentAsString(StandardCharsets.UTF_8), + "\"enterpriseNumber\""); + Assertions.assertTrue(count <= 20, "LIMIT 20 校验失败,count=" + count); + } + + // ==================== 工具方法 ==================== + + /** + * 统计字符串中子串出现次数 + */ + private int countOccurrences(String text, String sub) { + if (text == null || sub == null || sub.isEmpty()) { + return 0; + } + int count = 0; + int idx = 0; + while ((idx = text.indexOf(sub, idx)) != -1) { + count++; + idx += sub.length(); + } + return count; + } +}