feat(test-report): 支持xls和doc格式文件上传。优化台账管理功能,即监测点的word附件可以为空。

This commit is contained in:
dk
2026-07-21 10:19:29 +08:00
parent dbd288faec
commit 598ff9ea81
7 changed files with 124 additions and 48 deletions

View File

@@ -13,11 +13,11 @@ public final class AiReportFileExtensionConst {
private AiReportFileExtensionConst() {
}
public static final Set<String> ALLOWED_EXTENSIONS = unmodifiable("docx", "xlsx", "pdf");
public static final Set<String> ALLOWED_EXTENSIONS = unmodifiable("docx", "xls", "xlsx", "pdf");
public static final Set<String> WORD_EXTENSIONS = unmodifiable("docx");
public static final Set<String> EXCEL_EXTENSIONS = unmodifiable("xlsx");
public static final Set<String> EXCEL_EXTENSIONS = unmodifiable("xls", "xlsx");
public static final Set<String> PDF_EXTENSIONS = unmodifiable("pdf");

View File

@@ -65,12 +65,12 @@ public class TestReportLedgerExcelParser {
public ParsedLedger parseForValidate(String fileName, InputStream inputStream) {
String normalizedFileName = StrUtil.trimToNull(fileName);
if (!TestReportConst.LEDGER_SUMMARY_FILE_NAME.equals(normalizedFileName)) {
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:台账汇总文件名必须为台账信息汇总.xlsx");
if (!TestReportConst.isLedgerSummaryFileName(normalizedFileName)) {
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:台账汇总文件名必须为台账信息汇总.xls或台账信息汇总.xlsx");
}
try (Workbook workbook = WorkbookFactory.create(inputStream)) {
ParsedLedger ledger = new ParsedLedger();
ledger.setSummaryFileName(TestReportConst.LEDGER_SUMMARY_FILE_NAME);
ledger.setSummaryFileName(normalizedFileName);
ledger.getRequiredSheets().addAll(REQUIRED_SHEETS);
collectSheets(ledger, workbook);
@@ -139,11 +139,13 @@ public class TestReportLedgerExcelParser {
point.setTestDeviceNo(optionalCell(row, 3));
point.setClientUnitName(optionalCell(row, 4));
point.setExcelAttachmentName(requireCell(row, 5, rowIndex, "Excel附件名称"));
point.setWordAttachmentName(requireCell(row, 6, rowIndex, "Word附件名称"));
point.setWordAttachmentName(optionalCell(row, 6));
validateExtension(point.getExcelAttachmentName(), Arrays.asList(".xls", ".xlsx"),
"" + (rowIndex + 1) + "行Excel附件扩展名仅允许xls/xlsx");
if (StrUtil.isNotBlank(point.getWordAttachmentName())) {
validateExtension(point.getWordAttachmentName(), Arrays.asList(".doc", ".docx"),
"" + (rowIndex + 1) + "行Word附件扩展名仅允许doc/docx");
}
String pointKey = point.getSubstationName() + "|" + point.getPointName();
if (!pointKeys.add(pointKey)) {
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:监测点信息中存在重复点位");
@@ -151,7 +153,7 @@ public class TestReportLedgerExcelParser {
if (!excelNames.add(point.getExcelAttachmentName())) {
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段监测点信息中存在重复Excel附件名称");
}
if (!wordNames.add(point.getWordAttachmentName())) {
if (StrUtil.isNotBlank(point.getWordAttachmentName()) && !wordNames.add(point.getWordAttachmentName())) {
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段监测点信息中存在重复Word附件名称");
}
points.add(point);
@@ -229,7 +231,7 @@ public class TestReportLedgerExcelParser {
if (!attachmentNames.contains(point.getExcelAttachmentName())) {
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:第" + point.getRowNo() + "行缺少Excel附件");
}
if (!attachmentNames.contains(point.getWordAttachmentName())) {
if (StrUtil.isNotBlank(point.getWordAttachmentName()) && !attachmentNames.contains(point.getWordAttachmentName())) {
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:第" + point.getRowNo() + "行缺少Word附件");
}
}

View File

@@ -78,24 +78,25 @@ public class TestReportLedgerImportService {
TestReportStorageService.UploadedBatch uploadedBatch = testReportStorageService.saveValidateSessionFiles(sessionId, fileMap);
List<String> sessionFileNames = testReportStorageService.listValidateSessionFileNames(sessionId);
Set<String> sessionFileNameSet = new HashSet<String>(sessionFileNames);
String summaryFileName = resolveSummaryFileName(sessionFileNameSet);
TestReportLedgerValidateResultVO result = new TestReportLedgerValidateResultVO();
result.setSessionId(sessionId);
result.setTempStoragePath(uploadedBatch.getTempStoragePath());
result.setUploadedFileCount(sessionFileNames.size());
result.setCanContinueUpload(Boolean.TRUE);
result.setSummaryFileName(TestReportConst.LEDGER_SUMMARY_FILE_NAME);
result.setSummaryFilePresent(sessionFileNameSet.contains(TestReportConst.LEDGER_SUMMARY_FILE_NAME));
result.setSummaryFileName(summaryFileName);
result.setSummaryFilePresent(summaryFileName != null);
if (!Boolean.TRUE.equals(result.getSummaryFilePresent())) {
result.setSuccess(Boolean.FALSE);
result.getFailReasons().add("鏍¢獙鏂囦欢闃舵锛氱己灏戝彴璐︿俊鎭眹鎬?xlsx");
result.getFailReasons().add("校验文件阶段:缺少台账信息汇总.xls或台账信息汇总.xlsx");
persistValidateSessionResult(sessionId, result);
return result;
}
try (AiReportMinioStorageService.StoredObject storedObject =
testReportStorageService.getValidateSessionObject(sessionId, TestReportConst.LEDGER_SUMMARY_FILE_NAME);
testReportStorageService.getValidateSessionObject(sessionId, summaryFileName);
InputStream inputStream = storedObject.getInputStream()) {
TestReportLedgerExcelParser.ParsedLedger ledger = testReportLedgerExcelParser.parseForValidate(
TestReportConst.LEDGER_SUMMARY_FILE_NAME, inputStream);
summaryFileName, inputStream);
Map<String, TestDevicePO> dbDeviceMap = loadExistingDevices(collectDeviceNos(ledger));
Map<String, ClientUnitPO> dbClientMap = loadExistingClients(collectClientNames(ledger));
result.getRequiredSheets().addAll(ledger.getRequiredSheets());
@@ -106,10 +107,10 @@ public class TestReportLedgerImportService {
result.setSuccess(result.getMissingSheets().isEmpty() && result.getMissingAttachmentCount() != null
&& result.getMissingAttachmentCount() == 0 && !hasReferenceErrors(result.getPoints()));
if (!result.getMissingSheets().isEmpty()) {
result.getFailReasons().add("鏍¢獙鏂囦欢闃舵锛歋heet涓嶄笉榻? " + String.join("銆?", result.getMissingSheets()));
result.getFailReasons().add("校验文件阶段缺少必需Sheet" + String.join("", result.getMissingSheets()));
}
if (result.getMissingAttachmentCount() != null && result.getMissingAttachmentCount() > 0) {
result.getFailReasons().add("鏍¢獙鏂囦欢闃舵锛氬瓨鍦ㄧ己澶遍檮浠讹紝鍙户缁笂浼犲悗鍐嶆鏍¢獙");
result.getFailReasons().add("校验文件阶段:存在缺失附件,可继续上传后再次校验");
}
appendReferenceFailReasons(result);
persistValidateSessionResult(sessionId, result);
@@ -117,7 +118,7 @@ public class TestReportLedgerImportService {
} catch (BusinessException exception) {
throw exception;
} catch (Exception exception) {
throw new BusinessException(CommonResponseEnum.FAIL, "鏍¢獙鏂囦欢闃舵锛氳鍙栧彴璐︽眹鎬籩xcel澶辫触");
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段读取台账汇总excel失败");
}
}
@@ -126,9 +127,9 @@ public class TestReportLedgerImportService {
String batchId = UUID.randomUUID().toString();
TestReportStorageService.UploadedBatch uploadedBatch = testReportStorageService.saveTempBatchFiles(report.getId(), batchId, fileMap);
try {
MultipartFile summaryFile = fileMap.get(TestReportConst.LEDGER_SUMMARY_FILE_NAME);
if (summaryFile == null) {
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:缺少台账信息汇总.xlsx");
String summaryFileName = resolveSummaryFileName(fileMap.keySet());
if (summaryFileName == null) {
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:缺少台账信息汇总.xls或台账信息汇总.xlsx");
}
TestReportLedgerExcelParser.ParsedLedger ledger = parseLedgerFromBatch(report.getId(), batchId, fileMap.keySet());
Map<String, TestDevicePO> dbDeviceMap = loadExistingDevices(collectDeviceNos(ledger));
@@ -170,10 +171,14 @@ public class TestReportLedgerImportService {
}
private TestReportLedgerExcelParser.ParsedLedger parseLedgerFromBatch(String reportId, String batchId, Collection<String> fileNames) {
String summaryFileName = resolveSummaryFileName(fileNames);
if (StrUtil.isBlank(summaryFileName)) {
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:缺少台账信息汇总.xls或台账信息汇总.xlsx");
}
try (AiReportMinioStorageService.StoredObject storedObject =
testReportStorageService.getTempBatchObject(reportId, batchId, TestReportConst.LEDGER_SUMMARY_FILE_NAME);
testReportStorageService.getTempBatchObject(reportId, batchId, summaryFileName);
InputStream inputStream = storedObject.getInputStream()) {
return testReportLedgerExcelParser.parse(TestReportConst.LEDGER_SUMMARY_FILE_NAME, inputStream,
return testReportLedgerExcelParser.parse(summaryFileName, inputStream,
buildAttachmentNameSet(fileNames));
} catch (Exception exception) {
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段读取台账汇总excel失败");
@@ -186,7 +191,7 @@ public class TestReportLedgerImportService {
return attachmentNames;
}
for (String fileName : fileNames) {
if (StrUtil.isBlank(fileName) || TestReportConst.LEDGER_SUMMARY_FILE_NAME.equals(fileName)) {
if (StrUtil.isBlank(fileName) || TestReportConst.isLedgerSummaryFileName(fileName)) {
continue;
}
attachmentNames.add(fileName);
@@ -194,6 +199,24 @@ public class TestReportLedgerImportService {
return attachmentNames;
}
private String resolveSummaryFileName(Collection<String> fileNames) {
if (fileNames == null || fileNames.isEmpty()) {
return null;
}
String matchedFileName = null;
for (String fileName : fileNames) {
if (!TestReportConst.isLedgerSummaryFileName(fileName)) {
continue;
}
if (matchedFileName != null && !matchedFileName.equals(fileName)) {
throw new BusinessException(CommonResponseEnum.FAIL,
"校验文件阶段:台账汇总文件仅允许上传一个,文件名必须为台账信息汇总.xls或台账信息汇总.xlsx");
}
matchedFileName = fileName;
}
return matchedFileName;
}
private void fillPointValidationResult(TestReportLedgerValidateResultVO result,
TestReportLedgerExcelParser.ParsedLedger ledger,
Set<String> sessionFileNameSet,
@@ -211,19 +234,21 @@ public class TestReportLedgerImportService {
TestReportLedgerValidateAttachmentVO excelAttachment = buildAttachmentRow(
pointRow.getExcelAttachmentName(), "excel", sessionFileNameSet.contains(pointRow.getExcelAttachmentName()));
TestReportLedgerValidateAttachmentVO wordAttachment = buildAttachmentRow(
pointRow.getWordAttachmentName(), "word", sessionFileNameSet.contains(pointRow.getWordAttachmentName()));
pointVO.getAttachments().add(excelAttachment);
pointVO.getAttachments().add(wordAttachment);
int pointMissingCount = 0;
if (Boolean.TRUE.equals(excelAttachment.getMissing())) {
pointMissingCount++;
}
if (StrUtil.isNotBlank(pointRow.getWordAttachmentName())) {
TestReportLedgerValidateAttachmentVO wordAttachment = buildAttachmentRow(
pointRow.getWordAttachmentName(), "word", sessionFileNameSet.contains(pointRow.getWordAttachmentName()));
pointVO.getAttachments().add(wordAttachment);
if (Boolean.TRUE.equals(wordAttachment.getMissing())) {
pointMissingCount++;
}
}
pointVO.setMissingAttachmentCount(pointMissingCount);
fillReferenceValidationResult(pointVO, pointRow, dbDeviceMap, dbClientMap, sheetDeviceMap, sheetClientMap);
pointVO.setComplete(pointMissingCount == 0
@@ -273,10 +298,23 @@ public class TestReportLedgerImportService {
attachmentVO.setUploaded(uploaded);
attachmentVO.setMissing(!uploaded);
attachmentVO.setReuploadAllowed(Boolean.TRUE);
attachmentVO.setMessage(uploaded ? "宸蹭笂浼?" : "缂哄皯闄勪欢锛屽彲缁х画涓婁紶");
attachmentVO.setMessage(uploaded ? "已上传" : "缺少附件,可继续上传");
return attachmentVO;
}
private int countWordAttachments(TestReportLedgerExcelParser.ParsedLedger ledger) {
if (ledger == null || ledger.getPoints() == null || ledger.getPoints().isEmpty()) {
return 0;
}
int count = 0;
for (TestReportLedgerExcelParser.ParsedPointRow point : ledger.getPoints()) {
if (StrUtil.isNotBlank(point.getWordAttachmentName())) {
count++;
}
}
return count;
}
private List<String> resolveOrphanAttachmentNames(TestReportLedgerExcelParser.ParsedLedger ledger, Set<String> sessionFileNameSet) {
Set<String> referencedNames = new HashSet<String>();
for (TestReportLedgerExcelParser.ParsedPointRow pointRow : ledger.getPoints()) {
@@ -289,7 +327,7 @@ public class TestReportLedgerImportService {
}
List<String> orphanAttachmentNames = new java.util.ArrayList<String>();
for (String fileName : sessionFileNameSet) {
if (TestReportConst.LEDGER_SUMMARY_FILE_NAME.equals(fileName)) {
if (TestReportConst.isLedgerSummaryFileName(fileName)) {
continue;
}
if (!referencedNames.contains(fileName)) {
@@ -303,7 +341,7 @@ public class TestReportLedgerImportService {
try {
testReportStorageService.writeValidateSessionResult(sessionId, OBJECT_MAPPER.writeValueAsString(result));
} catch (Exception exception) {
throw new BusinessException(CommonResponseEnum.FAIL, "鏍¢獙鏂囦欢闃舵锛氫繚瀛樻牎楠岀粨鏋滃け璐?");
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:保存校验结果失败");
}
}
@@ -593,7 +631,7 @@ public class TestReportLedgerImportService {
result.setTotalFileCount(uploadedFileCount);
result.setPointCount(ledger.getPoints().size());
result.setExcelAttachmentCount(ledger.getPoints().size());
result.setWordAttachmentCount(ledger.getPoints().size());
result.setWordAttachmentCount(countWordAttachments(ledger));
result.getStages().add(buildStage("文件选择", "文件选择完成"));
result.getStages().add(buildStage("文件校验", "文件校验通过"));
return result;
@@ -613,7 +651,7 @@ public class TestReportLedgerImportService {
result.setTotalFileCount(uploadedFileCount);
result.setPointCount(ledger.getPoints().size());
result.setExcelAttachmentCount(ledger.getPoints().size());
result.setWordAttachmentCount(ledger.getPoints().size());
result.setWordAttachmentCount(countWordAttachments(ledger));
result.setDeviceAddedCount(summary.getDeviceAddedCount());
result.setDeviceReuseCount(summary.getDeviceReuseCount());
result.setClientAddedCount(summary.getClientAddedCount());

View File

@@ -38,11 +38,11 @@ public class TestReportLedgerTemplateService {
private void writeGuideSheet(XSSFSheet sheet) {
Row row0 = sheet.createRow(0);
row0.createCell(0).setCellValue("1. 汇总文件名固定为台账信息汇总.xlsx");
row0.createCell(0).setCellValue("1. 汇总文件名固定为台账信息汇总,扩展名支持 .xls、.xlsx");
Row row1 = sheet.createRow(1);
row1.createCell(0).setCellValue("2. 监测点信息sheet必填检测设备/委托单位sheet按需填写");
Row row2 = sheet.createRow(2);
row2.createCell(0).setCellValue("3. 每个监测点必须同时填写Excel附件名称Word附件名称");
row2.createCell(0).setCellValue("3. 每个监测点必须填写Excel附件名称Word附件名称可按需填写");
Row row3 = sheet.createRow(3);
row3.createCell(0).setCellValue("4. 检测设备编号、委托单位名称优先匹配数据库未命中时再匹配本次sheet");
sheet.setColumnWidth(0, 120 * 256);

View File

@@ -14,6 +14,9 @@ import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
@@ -24,28 +27,34 @@ import java.util.Set;
@RequiredArgsConstructor
public class TestReportStorageService {
private static final Set<String> POINT_WORD_ATTACHMENT_EXTENSIONS = Collections.unmodifiableSet(
new LinkedHashSet<String>(Arrays.asList("doc", "docx")));
private static final Set<String> IMPORT_ALLOWED_EXTENSIONS = Collections.unmodifiableSet(
new LinkedHashSet<String>(Arrays.asList("doc", "docx", "xls", "xlsx", "pdf")));
private final AiReportMinioStorageService minioStorageService;
public String saveSourceExcel(String reportId, MultipartFile file) {
validateMultipartFile(file, AiReportFileExtensionConst.EXCEL_EXTENSIONS, "原始Excel文件不能为空", "原始Excel仅允许xlsx格式");
validateMultipartFile(file, AiReportFileExtensionConst.EXCEL_EXTENSIONS, "原始Excel文件不能为空", "原始Excel仅允许xls/xlsx格式");
return minioStorageService.saveMultipartFile(buildBusinessDir(reportId, TestReportConst.STORAGE_SOURCE_EXCEL_DIR),
null, file, false).getObjectName();
}
public String saveLedgerSummaryExcel(String reportId, MultipartFile file) {
validateMultipartFile(file, AiReportFileExtensionConst.EXCEL_EXTENSIONS, "台账汇总文件不能为空", "台账汇总仅允许xlsx格式");
validateMultipartFile(file, AiReportFileExtensionConst.EXCEL_EXTENSIONS, "台账汇总文件不能为空", "台账汇总仅允许xls/xlsx格式");
return minioStorageService.saveMultipartFile(buildBusinessDir(reportId, TestReportConst.STORAGE_LEDGER_SUMMARY_DIR),
null, file, false).getObjectName();
}
public String savePointExcelAttachment(String reportId, MultipartFile file) {
validateMultipartFile(file, AiReportFileExtensionConst.EXCEL_EXTENSIONS, "监测点Excel附件不能为空", "监测点Excel附件仅允许xlsx格式");
validateMultipartFile(file, AiReportFileExtensionConst.EXCEL_EXTENSIONS, "监测点Excel附件不能为空", "监测点Excel附件仅允许xls/xlsx格式");
return minioStorageService.saveMultipartFile(buildBusinessDir(reportId, TestReportConst.STORAGE_POINT_EXCEL_DIR),
null, file, false).getObjectName();
}
public String savePointWordAttachment(String reportId, MultipartFile file) {
validateMultipartFile(file, AiReportFileExtensionConst.WORD_EXTENSIONS, "监测点Word附件不能为空", "监测点Word附件仅允许docx格式");
validateMultipartFile(file, POINT_WORD_ATTACHMENT_EXTENSIONS, "监测点Word附件不能为空", "监测点Word附件仅允许doc/docx格式");
return minioStorageService.saveMultipartFile(buildBusinessDir(reportId, TestReportConst.STORAGE_POINT_WORD_DIR),
null, file, false).getObjectName();
}
@@ -91,19 +100,19 @@ public class TestReportStorageService {
public String saveLedgerSummaryFromTemp(String reportId, String batchId, String fileName) {
return copyTempBatchFile(reportId, batchId, fileName,
buildBusinessDir(reportId, TestReportConst.STORAGE_LEDGER_SUMMARY_DIR),
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "保存台账汇总excel失败");
resolveExcelContentType(fileName), "保存台账汇总excel失败");
}
public String savePointExcelFromTemp(String reportId, String batchId, String fileName) {
return copyTempBatchFile(reportId, batchId, fileName,
buildBusinessDir(reportId, TestReportConst.STORAGE_POINT_EXCEL_DIR),
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "保存监测点excel附件失败");
resolveExcelContentType(fileName), "保存监测点excel附件失败");
}
public String savePointWordFromTemp(String reportId, String batchId, String fileName) {
return copyTempBatchFile(reportId, batchId, fileName,
buildBusinessDir(reportId, TestReportConst.STORAGE_POINT_WORD_DIR),
"application/vnd.openxmlformats-officedocument.wordprocessingml.document", "保存监测点word附件失败");
resolveWordContentType(fileName), "保存监测点Word附件失败");
}
public String saveGeneratedPlaceholder(String reportId, String fileName, byte[] content) {
@@ -212,8 +221,24 @@ public class TestReportStorageService {
}
private void validateAllowedUploadFile(String fileName) {
validateFileName(fileName, AiReportFileExtensionConst.ALLOWED_EXTENSIONS, "文件不能为空",
"仅允许上传docx、xlsx、pdf文件");
validateFileName(fileName, IMPORT_ALLOWED_EXTENSIONS, "文件不能为空",
"仅允许上传doc、docx、xls、xlsx、pdf文件");
}
private String resolveExcelContentType(String fileName) {
String extension = resolveExtension(fileName);
if ("xls".equals(extension)) {
return "application/vnd.ms-excel";
}
return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
}
private String resolveWordContentType(String fileName) {
String extension = resolveExtension(fileName);
if ("doc".equals(extension)) {
return "application/msword";
}
return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
}
private void validateFileName(String fileName, Set<String> allowedExtensions,

View File

@@ -35,7 +35,9 @@ public final class TestReportConst {
public static final String STORAGE_VALIDATE_SESSION_DIR = "validate-session";
public static final String STORAGE_VALIDATE_RESULT_FILE_NAME = "validate-result.json";
public static final String LEDGER_SUMMARY_FILE_NAME = "台账信息汇总.xlsx";
public static final String LEDGER_SUMMARY_FILE_BASE_NAME = "台账信息汇总";
public static final String LEDGER_SUMMARY_FILE_NAME = LEDGER_SUMMARY_FILE_BASE_NAME + ".xlsx";
public static final String LEDGER_SUMMARY_FILE_NAME_XLS = LEDGER_SUMMARY_FILE_BASE_NAME + ".xls";
public static final String LEDGER_GUIDE_SHEET_NAME = "填写说明";
public static final String LEDGER_POINT_SHEET_NAME = "监测点信息";
public static final String LEDGER_DEVICE_SHEET_NAME = "检测设备";
@@ -43,4 +45,13 @@ public final class TestReportConst {
private TestReportConst() {
}
public static boolean isLedgerSummaryFileName(String fileName) {
if (fileName == null) {
return false;
}
String normalizedFileName = fileName.trim();
return LEDGER_SUMMARY_FILE_NAME.equalsIgnoreCase(normalizedFileName)
|| LEDGER_SUMMARY_FILE_NAME_XLS.equalsIgnoreCase(normalizedFileName);
}
}

View File

@@ -525,8 +525,8 @@ public class TestReportServiceImpl extends ServiceImpl<TestReportMapper, TestRep
throw new BusinessException(CommonResponseEnum.FAIL, MSG_IMPORT_FILES_REQUIRED);
}
String normalizedFileName = originalFileName.toLowerCase();
if (!normalizedFileName.endsWith(".xlsx")) {
throw new BusinessException(CommonResponseEnum.FAIL, "原始Excel仅允许xlsx格式");
if (!normalizedFileName.endsWith(".xls") && !normalizedFileName.endsWith(".xlsx")) {
throw new BusinessException(CommonResponseEnum.FAIL, "原始Excel仅允许xls/xlsx格式");
}
if (!fileNames.add(originalFileName)) {
throw new BusinessException(CommonResponseEnum.FAIL, MSG_IMPORT_FILE_NAME_DUPLICATE);