fix: 优化顶级部门存在和子部门重复时,用户导入错误的问题

This commit is contained in:
2026-08-13 22:30:45 +08:00
parent 64a85c6a5b
commit 9be9bd7665
4 changed files with 222 additions and 76 deletions

View File

@@ -17,7 +17,6 @@ import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.web.multipart.MultipartFile;
import tech.easyflow.common.constant.enums.EnumAccountType;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.web.exceptions.BusinessException;
@@ -40,19 +39,9 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.*;
/**
* {@link SysAccountServiceImpl} 测试。
@@ -174,7 +163,8 @@ public class SysAccountServiceImplTest {
List.of("技术部/研发", "tech-research", "技术研发", "普通员工"),
List.of(" 产品部 / 研发 ", "product-research", "产品研发", "普通员工"),
List.of("技术部/交付", "delivery", "交付人员", "普通员工"),
List.of("总公司", "head-office", "总部人员", "普通员工")
List.of("总公司", "head-office", "总部人员", "普通员工"),
List.of("交付", "delivery-short", "唯一名称兼容", "普通员工")
)
);
@@ -183,21 +173,88 @@ public class SysAccountServiceImplTest {
importLoginAccount()
);
assertEquals(4, result.getSuccessCount());
assertEquals(5, result.getSuccessCount());
assertEquals(0, result.getErrorCount());
ArgumentCaptor<SysAccount> accountCaptor = ArgumentCaptor.forClass(SysAccount.class);
verify(service, times(4)).save(accountCaptor.capture());
verify(service, times(5)).save(accountCaptor.capture());
assertEquals(
List.of(
BigInteger.valueOf(3),
BigInteger.valueOf(6),
BigInteger.valueOf(4),
BigInteger.ONE
BigInteger.ONE,
BigInteger.valueOf(4)
),
accountCaptor.getAllValues().stream().map(SysAccount::getDeptId).toList()
);
}
/**
* 验证当前模板优先把单段内容解析为顶级部门完整路径。
*
* @throws Exception 注入测试依赖或构造上传文件失败
*/
@Test
public void shouldPreferTopLevelPathWhenCurrentTemplateHasDuplicatedDepartmentName() throws Exception {
SysAccountServiceImpl service = createReadyImportService(List.of(
buildDept(1, 0, "交易银行部"),
buildDept(2, 0, "石家庄分行"),
buildDept(3, 2, "交易银行部"),
buildDept(4, 0, "济南分行"),
buildDept(5, 4, "交易银行部")
));
byte[] workbook = createImportWorkbook(
importHeaders("部门路径*"),
List.of(List.of(" 交易银行部 ", "head-office-trade", "总行交易银行", "普通员工"))
);
SysAccountImportResultVo result = service.importAccounts(
mockMultipartFile(workbook),
importLoginAccount()
);
assertEquals(1, result.getSuccessCount());
assertEquals(0, result.getErrorCount());
ArgumentCaptor<SysAccount> accountCaptor = ArgumentCaptor.forClass(SysAccount.class);
verify(service).save(accountCaptor.capture());
assertEquals(BigInteger.ONE, accountCaptor.getValue().getDeptId());
}
/**
* 验证当前模板无法按完整路径命中重名子部门时返回可填写的候选路径。
*
* @throws Exception 注入测试依赖或构造上传文件失败
*/
@Test
public void shouldListCandidatePathsForDuplicatedDepartmentName() throws Exception {
SysAccountServiceImpl service = createReadyImportService(List.of(
buildDept(1, 0, "石家庄分行"),
buildDept(2, 1, "交易银行部"),
buildDept(3, 0, "济南分行"),
buildDept(4, 3, "交易银行部")
));
byte[] workbook = createImportWorkbook(
importHeaders("部门路径*"),
List.of(List.of("交易银行部", "ambiguous-trade", "重名交易银行", "普通员工"))
);
SysAccountImportResultVo result = service.importAccounts(
mockMultipartFile(workbook),
importLoginAccount()
);
assertEquals(0, result.getSuccessCount());
assertEquals(1, result.getErrorCount());
String reason = result.getErrorRows().get(0).getDetails().stream()
.filter(detail -> "部门路径".equals(detail.getFieldName()))
.findFirst()
.orElseThrow()
.getReason();
assertTrue(reason.contains("石家庄分行/交易银行部"));
assertTrue(reason.contains("济南分行/交易银行部"));
verify(service, never()).save(any(SysAccount.class));
}
/**
* 验证错误地把并列顶级部门拼到总公司之后时不会发生误匹配。
*
@@ -266,6 +323,37 @@ public class SysAccountServiceImplTest {
assertEquals(BigInteger.valueOf(5), accountCaptor.getValue().getDeptId());
}
/**
* 验证旧部门名称表头不会把重名顶级部门静默解释为完整路径。
*
* @throws Exception 注入测试依赖或构造上传文件失败
*/
@Test
public void shouldKeepLegacyNameAmbiguityForDuplicatedTopLevelDepartment() throws Exception {
SysAccountServiceImpl service = createReadyImportService(List.of(
buildDept(1, 0, "交易银行部"),
buildDept(2, 0, "石家庄分行"),
buildDept(3, 2, "交易银行部")
));
byte[] workbook = createImportWorkbook(
importHeaders("部门名称*"),
List.of(List.of("交易银行部", "legacy-trade", "旧模板交易银行", "普通员工"))
);
SysAccountImportResultVo result = service.importAccounts(
mockMultipartFile(workbook),
importLoginAccount()
);
assertEquals(0, result.getSuccessCount());
assertEquals(1, result.getErrorCount());
assertTrue(result.getErrorRows().get(0).getDetails().stream().anyMatch(detail ->
detail.getReason().contains("交易银行部")
&& detail.getReason().contains("石家庄分行/交易银行部")
));
verify(service, never()).save(any(SysAccount.class));
}
/**
* 验证同一父部门下的同名子部门不会被完整路径静默匹配到任意一条记录。
*