调整实体库管理数据来源维护规则

This commit is contained in:
wkc
2026-04-23 17:31:56 +08:00
parent d444eafd5f
commit b7d020c0b2
12 changed files with 630 additions and 125 deletions

View File

@@ -89,7 +89,6 @@ public class CcdiEnterpriseBaseInfoAddDTO implements Serializable {
private String shareholder5;
@Schema(description = "经营状态")
@NotBlank(message = "经营状态不能为空")
@Size(max = 50, message = "经营状态长度不能超过50个字符")
private String status;
@@ -102,6 +101,5 @@ public class CcdiEnterpriseBaseInfoAddDTO implements Serializable {
private String entSource;
@Schema(description = "数据来源")
@NotBlank(message = "数据来源不能为空")
private String dataSource;
}

View File

@@ -89,7 +89,6 @@ public class CcdiEnterpriseBaseInfoEditDTO implements Serializable {
private String shareholder5;
@Schema(description = "经营状态")
@NotBlank(message = "经营状态不能为空")
@Size(max = 50, message = "经营状态长度不能超过50个字符")
private String status;

View File

@@ -88,7 +88,7 @@ public class CcdiEnterpriseBaseInfoExcel implements Serializable {
@ColumnWidth(18)
private String shareholder5;
@ExcelProperty(value = "经营状态*", index = 16)
@ExcelProperty(value = "经营状态", index = 16)
@ColumnWidth(16)
private String status;
@@ -99,8 +99,4 @@ public class CcdiEnterpriseBaseInfoExcel implements Serializable {
@ExcelProperty(value = "企业来源*", index = 18)
@ColumnWidth(18)
private String entSource;
@ExcelProperty(value = "数据来源*", index = 19)
@ColumnWidth(18)
private String dataSource;
}

View File

@@ -131,10 +131,6 @@ public class CcdiEnterpriseBaseInfoImportServiceImpl implements ICcdiEnterpriseB
if (!excel.getSocialCreditCode().matches("^[0-9A-HJ-NPQRTUWXY]{2}\\d{6}[0-9A-HJ-NPQRTUWXY]{10}$")) {
throw new RuntimeException("统一社会信用代码格式不正确");
}
if (StringUtils.isEmpty(excel.getStatus())) {
throw new RuntimeException("经营状态不能为空");
}
String riskLevel = EnterpriseRiskLevel.resolveCode(StringUtils.trim(excel.getRiskLevel()));
if (riskLevel == null) {
throw new RuntimeException("风险等级不在允许范围内");
@@ -143,10 +139,6 @@ public class CcdiEnterpriseBaseInfoImportServiceImpl implements ICcdiEnterpriseB
if (entSource == null) {
throw new RuntimeException("企业来源不在允许范围内");
}
String dataSource = resolveDataSourceCode(StringUtils.trim(excel.getDataSource()));
if (dataSource == null) {
throw new RuntimeException("数据来源不在允许范围内");
}
if (existingCreditCodes.contains(excel.getSocialCreditCode())) {
throw new RuntimeException(String.format("统一社会信用代码[%s]已存在,请勿重复导入", excel.getSocialCreditCode()));
@@ -159,8 +151,8 @@ public class CcdiEnterpriseBaseInfoImportServiceImpl implements ICcdiEnterpriseB
BeanUtils.copyProperties(excel, entity);
entity.setRiskLevel(riskLevel);
entity.setEntSource(entSource);
entity.setDataSource(dataSource);
entity.setStatus(StringUtils.trim(excel.getStatus()));
entity.setDataSource(DataSource.IMPORT.getCode());
entity.setStatus(trimToNull(excel.getStatus()));
entity.setCreatedBy(userName);
entity.setUpdatedBy(userName);
return entity;
@@ -206,15 +198,6 @@ public class CcdiEnterpriseBaseInfoImportServiceImpl implements ICcdiEnterpriseB
redisTemplate.opsForHash().putAll(buildStatusKey(taskId), statusData);
}
private String resolveDataSourceCode(String value) {
for (DataSource source : DataSource.values()) {
if (source.getCode().equals(value) || source.getDesc().equals(value)) {
return source.getCode();
}
}
return null;
}
private String buildStatusKey(String taskId) {
return "import:enterpriseBaseInfo:" + taskId;
}
@@ -222,4 +205,11 @@ public class CcdiEnterpriseBaseInfoImportServiceImpl implements ICcdiEnterpriseB
private String buildFailuresKey(String taskId) {
return "import:enterpriseBaseInfo:" + taskId + ":failures";
}
private String trimToNull(String value) {
if (StringUtils.isEmpty(value)) {
return null;
}
return value.trim();
}
}

View File

@@ -85,10 +85,12 @@ public class CcdiEnterpriseBaseInfoServiceImpl implements ICcdiEnterpriseBaseInf
if (enterpriseBaseInfoMapper.selectById(addDTO.getSocialCreditCode()) != null) {
throw new RuntimeException("该统一社会信用代码已存在");
}
validateEnumFields(addDTO.getStatus(), addDTO.getRiskLevel(), addDTO.getEntSource(), addDTO.getDataSource());
validateRiskLevelAndEnterpriseSource(addDTO.getRiskLevel(), addDTO.getEntSource());
CcdiEnterpriseBaseInfo entity = new CcdiEnterpriseBaseInfo();
BeanUtils.copyProperties(addDTO, entity);
entity.setStatus(trimToNull(addDTO.getStatus()));
entity.setDataSource(DataSource.MANUAL.getCode());
return enterpriseBaseInfoMapper.insert(entity);
}
@@ -103,6 +105,8 @@ public class CcdiEnterpriseBaseInfoServiceImpl implements ICcdiEnterpriseBaseInf
CcdiEnterpriseBaseInfo entity = new CcdiEnterpriseBaseInfo();
BeanUtils.copyProperties(editDTO, entity);
entity.setStatus(trimToNull(editDTO.getStatus()));
entity.setDataSource(existing.getDataSource());
return enterpriseBaseInfoMapper.updateById(entity);
}
@@ -176,18 +180,22 @@ public class CcdiEnterpriseBaseInfoServiceImpl implements ICcdiEnterpriseBaseInf
}
private void validateEnumFields(String status, String riskLevel, String entSource, String dataSource) {
if (StringUtils.isEmpty(status)) {
throw new RuntimeException("经营状态不能为空");
validateRiskLevelAndEnterpriseSource(riskLevel, entSource);
if (StringUtils.isNotEmpty(status) && StringUtils.trim(status).length() > 50) {
throw new RuntimeException("经营状态长度不能超过50个字符");
}
if (!containsDataSource(dataSource)) {
throw new RuntimeException("数据来源不在允许范围内");
}
}
private void validateRiskLevelAndEnterpriseSource(String riskLevel, String entSource) {
if (!EnterpriseRiskLevel.contains(riskLevel)) {
throw new RuntimeException("风险等级不在允许范围内");
}
if (!EnterpriseSource.contains(entSource)) {
throw new RuntimeException("企业来源不在允许范围内");
}
if (!containsDataSource(dataSource)) {
throw new RuntimeException("数据来源不在允许范围内");
}
}
private boolean containsDataSource(String code) {
@@ -199,6 +207,13 @@ public class CcdiEnterpriseBaseInfoServiceImpl implements ICcdiEnterpriseBaseInf
return false;
}
private String trimToNull(String value) {
if (StringUtils.isEmpty(value)) {
return null;
}
return value.trim();
}
private void validateDeleteRelations(String socialCreditCode) {
StringJoiner relationTypes = new StringJoiner("");
if (staffEnterpriseRelationMapper.selectCount(new LambdaQueryWrapper<CcdiStaffEnterpriseRelation>()

View File

@@ -0,0 +1,77 @@
package com.ruoyi.info.collection.service;
import com.ruoyi.info.collection.domain.CcdiEnterpriseBaseInfo;
import com.ruoyi.info.collection.domain.excel.CcdiEnterpriseBaseInfoExcel;
import com.ruoyi.info.collection.service.impl.CcdiEnterpriseBaseInfoImportServiceImpl;
import org.junit.jupiter.api.Test;
import java.util.HashSet;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
class CcdiEnterpriseBaseInfoImportServiceImplTest {
private final CcdiEnterpriseBaseInfoImportServiceImpl service = new CcdiEnterpriseBaseInfoImportServiceImpl();
@Test
void validateAndBuildEntity_shouldRejectWhenDatabaseAlreadyContainsCreditCode() {
CcdiEnterpriseBaseInfoExcel excel = buildExcel();
RuntimeException exception = assertThrows(RuntimeException.class,
() -> service.validateAndBuildEntity(excel, Set.of("91310000123456789A"), new HashSet<>(), "admin"));
assertEquals("统一社会信用代码[91310000123456789A]已存在,请勿重复导入", exception.getMessage());
}
@Test
void validateAndBuildEntity_shouldRejectWhenExcelContainsDuplicateCreditCode() {
CcdiEnterpriseBaseInfoExcel excel = buildExcel();
RuntimeException exception = assertThrows(RuntimeException.class,
() -> service.validateAndBuildEntity(excel, Set.of(), new HashSet<>(Set.of("91310000123456789A")), "admin"));
assertEquals("统一社会信用代码[91310000123456789A]在导入文件中重复,已跳过此条记录", exception.getMessage());
}
@Test
void validateAndBuildEntity_shouldNormalizeEnumTextToCode() {
CcdiEnterpriseBaseInfoExcel excel = buildExcel();
excel.setRiskLevel("高风险");
excel.setEntSource("一般企业");
CcdiEnterpriseBaseInfo entity = service.validateAndBuildEntity(excel, Set.of(), new HashSet<>(), "admin");
assertEquals("1", entity.getRiskLevel());
assertEquals("GENERAL", entity.getEntSource());
assertEquals("IMPORT", entity.getDataSource());
assertEquals("admin", entity.getCreatedBy());
}
@Test
void validateAndBuildEntity_shouldAllowBlankStatus() {
CcdiEnterpriseBaseInfoExcel excel = buildExcel();
excel.setStatus(null);
CcdiEnterpriseBaseInfo entity = service.validateAndBuildEntity(excel, Set.of(), new HashSet<>(), "admin");
assertNull(entity.getStatus());
assertEquals("IMPORT", entity.getDataSource());
}
private CcdiEnterpriseBaseInfoExcel buildExcel() {
CcdiEnterpriseBaseInfoExcel excel = new CcdiEnterpriseBaseInfoExcel();
excel.setSocialCreditCode("91310000123456789A");
excel.setEnterpriseName("测试企业");
excel.setEnterpriseType("有限责任公司");
excel.setEnterpriseNature("民营企业");
excel.setIndustryClass("制造业");
excel.setIndustryName("电子设备");
excel.setStatus("存续");
excel.setRiskLevel("1");
excel.setEntSource("GENERAL");
return excel;
}
}

View File

@@ -0,0 +1,204 @@
package com.ruoyi.info.collection.service;
import com.ruoyi.info.collection.domain.CcdiEnterpriseBaseInfo;
import com.ruoyi.info.collection.domain.dto.CcdiEnterpriseBaseInfoAddDTO;
import com.ruoyi.info.collection.domain.dto.CcdiEnterpriseBaseInfoEditDTO;
import com.ruoyi.info.collection.domain.vo.CcdiEnterpriseBaseInfoVO;
import com.ruoyi.info.collection.mapper.CcdiCustEnterpriseRelationMapper;
import com.ruoyi.info.collection.mapper.CcdiEnterpriseBaseInfoMapper;
import com.ruoyi.info.collection.mapper.CcdiIntermediaryEnterpriseRelationMapper;
import com.ruoyi.info.collection.mapper.CcdiStaffEnterpriseRelationMapper;
import com.ruoyi.info.collection.service.impl.CcdiEnterpriseBaseInfoServiceImpl;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.data.redis.core.RedisTemplate;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class CcdiEnterpriseBaseInfoServiceImplTest {
@InjectMocks
private CcdiEnterpriseBaseInfoServiceImpl service;
@Mock
private CcdiEnterpriseBaseInfoMapper enterpriseBaseInfoMapper;
@Mock
private CcdiStaffEnterpriseRelationMapper staffEnterpriseRelationMapper;
@Mock
private CcdiCustEnterpriseRelationMapper custEnterpriseRelationMapper;
@Mock
private CcdiIntermediaryEnterpriseRelationMapper intermediaryEnterpriseRelationMapper;
@Mock
private ICcdiEnterpriseBaseInfoImportService enterpriseBaseInfoImportService;
@Mock
private RedisTemplate<String, Object> redisTemplate;
@Test
void insertEnterpriseBaseInfo_shouldPersistWhenSocialCreditCodeIsUnique() {
CcdiEnterpriseBaseInfoAddDTO addDTO = buildAddDto();
when(enterpriseBaseInfoMapper.selectById(addDTO.getSocialCreditCode())).thenReturn(null);
when(enterpriseBaseInfoMapper.insert(any(CcdiEnterpriseBaseInfo.class))).thenReturn(1);
int result = service.insertEnterpriseBaseInfo(addDTO);
assertEquals(1, result);
ArgumentCaptor<CcdiEnterpriseBaseInfo> captor = ArgumentCaptor.forClass(CcdiEnterpriseBaseInfo.class);
verify(enterpriseBaseInfoMapper).insert(captor.capture());
assertEquals("测试企业", captor.getValue().getEnterpriseName());
assertEquals("GENERAL", captor.getValue().getEntSource());
}
@Test
void insertEnterpriseBaseInfo_shouldSetManualDataSourceAndAllowBlankStatus() {
CcdiEnterpriseBaseInfoAddDTO addDTO = buildAddDto();
addDTO.setStatus(null);
addDTO.setDataSource("API");
when(enterpriseBaseInfoMapper.selectById(addDTO.getSocialCreditCode())).thenReturn(null);
when(enterpriseBaseInfoMapper.insert(any(CcdiEnterpriseBaseInfo.class))).thenReturn(1);
int result = service.insertEnterpriseBaseInfo(addDTO);
assertEquals(1, result);
ArgumentCaptor<CcdiEnterpriseBaseInfo> captor = ArgumentCaptor.forClass(CcdiEnterpriseBaseInfo.class);
verify(enterpriseBaseInfoMapper).insert(captor.capture());
assertEquals("MANUAL", captor.getValue().getDataSource());
assertNull(captor.getValue().getStatus());
}
@Test
void insertEnterpriseBaseInfo_shouldRejectInvalidRiskLevel() {
CcdiEnterpriseBaseInfoAddDTO addDTO = buildAddDto();
addDTO.setRiskLevel("9");
when(enterpriseBaseInfoMapper.selectById(addDTO.getSocialCreditCode())).thenReturn(null);
RuntimeException exception = assertThrows(RuntimeException.class,
() -> service.insertEnterpriseBaseInfo(addDTO));
assertEquals("风险等级不在允许范围内", exception.getMessage());
}
@Test
void updateEnterpriseBaseInfo_shouldRejectWhenRecordMissing() {
CcdiEnterpriseBaseInfoEditDTO editDTO = buildEditDto();
when(enterpriseBaseInfoMapper.selectById(editDTO.getSocialCreditCode())).thenReturn(null);
RuntimeException exception = assertThrows(RuntimeException.class,
() -> service.updateEnterpriseBaseInfo(editDTO));
assertEquals("实体库记录不存在", exception.getMessage());
}
@Test
void updateEnterpriseBaseInfo_shouldKeepExistingDataSource() {
CcdiEnterpriseBaseInfoEditDTO editDTO = buildEditDto();
editDTO.setDataSource("API");
CcdiEnterpriseBaseInfo existing = new CcdiEnterpriseBaseInfo();
existing.setSocialCreditCode(editDTO.getSocialCreditCode());
existing.setDataSource("MANUAL");
when(enterpriseBaseInfoMapper.selectById(editDTO.getSocialCreditCode())).thenReturn(existing);
when(enterpriseBaseInfoMapper.updateById(any(CcdiEnterpriseBaseInfo.class))).thenReturn(1);
int result = service.updateEnterpriseBaseInfo(editDTO);
assertEquals(1, result);
ArgumentCaptor<CcdiEnterpriseBaseInfo> captor = ArgumentCaptor.forClass(CcdiEnterpriseBaseInfo.class);
verify(enterpriseBaseInfoMapper).updateById(captor.capture());
assertEquals("MANUAL", captor.getValue().getDataSource());
}
@Test
void selectEnterpriseBaseInfoById_shouldConvertEntityToVo() {
CcdiEnterpriseBaseInfo entity = new CcdiEnterpriseBaseInfo();
entity.setSocialCreditCode("91310000123456789A");
entity.setEnterpriseName("测试企业");
entity.setRiskLevel("1");
entity.setEntSource("GENERAL");
when(enterpriseBaseInfoMapper.selectById("91310000123456789A")).thenReturn(entity);
CcdiEnterpriseBaseInfoVO vo = service.selectEnterpriseBaseInfoById("91310000123456789A");
assertNotNull(vo);
assertEquals("测试企业", vo.getEnterpriseName());
assertEquals("1", vo.getRiskLevel());
}
@Test
void deleteEnterpriseBaseInfoByIds_shouldDeleteInBatch() {
when(staffEnterpriseRelationMapper.selectCount(any())).thenReturn(0L);
when(custEnterpriseRelationMapper.selectCount(any())).thenReturn(0L);
when(intermediaryEnterpriseRelationMapper.selectCount(any())).thenReturn(0L);
when(enterpriseBaseInfoMapper.deleteBatchIds(java.util.List.of("91310000123456789A", "91310000123456789B")))
.thenReturn(2);
int result = service.deleteEnterpriseBaseInfoByIds(new String[]{"91310000123456789A", "91310000123456789B"});
assertEquals(2, result);
verify(enterpriseBaseInfoMapper).deleteBatchIds(java.util.List.of("91310000123456789A", "91310000123456789B"));
}
@Test
void deleteEnterpriseBaseInfoByIds_shouldRejectWhenStaffRelationExists() {
when(staffEnterpriseRelationMapper.selectCount(any())).thenReturn(1L);
when(custEnterpriseRelationMapper.selectCount(any())).thenReturn(0L);
when(intermediaryEnterpriseRelationMapper.selectCount(any())).thenReturn(0L);
RuntimeException exception = assertThrows(RuntimeException.class,
() -> service.deleteEnterpriseBaseInfoByIds(new String[]{"91310000123456789A"}));
assertEquals("统一社会信用代码[91310000123456789A]已关联员工,删除失败", exception.getMessage());
}
@Test
void deleteEnterpriseBaseInfoByIds_shouldRejectWhenMultipleRelationsExist() {
when(staffEnterpriseRelationMapper.selectCount(any())).thenReturn(1L);
when(custEnterpriseRelationMapper.selectCount(any())).thenReturn(1L);
when(intermediaryEnterpriseRelationMapper.selectCount(any())).thenReturn(1L);
RuntimeException exception = assertThrows(RuntimeException.class,
() -> service.deleteEnterpriseBaseInfoByIds(new String[]{"91310000123456789A"}));
assertEquals("统一社会信用代码[91310000123456789A]已关联员工、信贷客户、中介,删除失败", exception.getMessage());
}
private CcdiEnterpriseBaseInfoAddDTO buildAddDto() {
CcdiEnterpriseBaseInfoAddDTO dto = new CcdiEnterpriseBaseInfoAddDTO();
dto.setSocialCreditCode("91310000123456789A");
dto.setEnterpriseName("测试企业");
dto.setEnterpriseType("有限责任公司");
dto.setEnterpriseNature("民营企业");
dto.setIndustryClass("制造业");
dto.setIndustryName("电子设备");
dto.setStatus("存续");
dto.setRiskLevel("1");
dto.setEntSource("GENERAL");
dto.setDataSource("MANUAL");
return dto;
}
private CcdiEnterpriseBaseInfoEditDTO buildEditDto() {
CcdiEnterpriseBaseInfoEditDTO dto = new CcdiEnterpriseBaseInfoEditDTO();
dto.setSocialCreditCode("91310000123456789A");
dto.setEnterpriseName("测试企业");
dto.setStatus("存续");
dto.setRiskLevel("1");
dto.setEntSource("GENERAL");
dto.setDataSource("MANUAL");
return dto;
}
}

View File

@@ -4,11 +4,13 @@ import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.info.collection.domain.excel.CcdiBaseStaffExcel;
import com.ruoyi.info.collection.domain.excel.CcdiAssetInfoExcel;
import com.ruoyi.info.collection.domain.excel.CcdiEnterpriseBaseInfoExcel;
import com.ruoyi.info.collection.domain.excel.CcdiStaffRecruitmentExcel;
import com.ruoyi.info.collection.domain.excel.CcdiStaffRecruitmentWorkExcel;
import com.ruoyi.info.collection.domain.excel.CcdiStaffFmyRelationExcel;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
@@ -129,6 +131,30 @@ class EasyExcelUtilTemplateTest {
}
}
@Test
void importTemplateWithDictDropdown_shouldUseUpdatedEnterpriseHeaders() throws Exception {
MockHttpServletResponse response = new MockHttpServletResponse();
try (MockedStatic<DictUtils> mocked = mockStatic(DictUtils.class)) {
mocked.when(() -> DictUtils.getDictCache("ccdi_entity_type"))
.thenReturn(List.of(buildDictData("有限责任公司")));
mocked.when(() -> DictUtils.getDictCache("ccdi_enterprise_nature"))
.thenReturn(List.of(buildDictData("民营企业")));
mocked.when(() -> DictUtils.getDictCache("ccdi_certificate_type"))
.thenReturn(List.of(buildDictData("居民身份证")));
EasyExcelUtil.importTemplateWithDictDropdown(response, CcdiEnterpriseBaseInfoExcel.class, "实体库管理");
}
try (Workbook workbook = WorkbookFactory.create(new ByteArrayInputStream(response.getContentAsByteArray()))) {
Row headerRow = workbook.getSheetAt(0).getRow(0);
assertEquals("经营状态", headerRow.getCell(16).getStringCellValue());
assertEquals("风险等级*", headerRow.getCell(17).getStringCellValue());
assertEquals("企业来源*", headerRow.getCell(18).getStringCellValue());
assertEquals(19, headerRow.getLastCellNum());
}
}
private void assertTextColumn(Sheet sheet, int columnIndex) {
CellStyle style = sheet.getColumnStyle(columnIndex);
assertNotNull(style, "文本列应设置默认样式");