feat(test-report): 重构台账导入功能并增加验证机制
- 新增导入临时目录、验证会话等常量配置 - 实现台账文件分步验证流程,支持先验证后导入模式 - 在控制器层新增validateLedger接口用于文件校验 - 修改importLedger接口参数结构,改为导入已验证批次 - 重构TestReportLedgerExcelParser解析器,支持独立验证和解析 - 新增验证结果对象TestReportLedgerValidateResultVO - 实现台账文件批量存储和临时文件管理机制 - 完善文件上传、校验、导入各阶段的状态跟踪 - 优化错误提示信息,提供更详细的验证反馈 - 更新导入成功结果对象,增加批次ID和存储路径信息
This commit is contained in:
@@ -33,6 +33,11 @@ public class TestReportLedgerExcelParser {
|
||||
|
||||
private static final DataFormatter FORMATTER = new DataFormatter();
|
||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
private static final List<String> REQUIRED_SHEETS = Arrays.asList(
|
||||
TestReportConst.LEDGER_GUIDE_SHEET_NAME,
|
||||
TestReportConst.LEDGER_POINT_SHEET_NAME,
|
||||
TestReportConst.LEDGER_DEVICE_SHEET_NAME,
|
||||
TestReportConst.LEDGER_CLIENT_SHEET_NAME);
|
||||
private static final List<String> POINT_HEADERS = Arrays.asList(
|
||||
"分组号", "变电站", "监测点名称", "检测设备编号", "委托单位名称", "Excel附件名称", "Word附件名称");
|
||||
private static final List<String> DEVICE_HEADERS = Arrays.asList(
|
||||
@@ -42,17 +47,44 @@ public class TestReportLedgerExcelParser {
|
||||
|
||||
public ParsedLedger parse(MultipartFile summaryFile, Map<String, MultipartFile> attachments) {
|
||||
String fileName = summaryFile == null ? null : StrUtil.trimToNull(summaryFile.getOriginalFilename());
|
||||
if (!TestReportConst.LEDGER_SUMMARY_FILE_NAME.equals(fileName)) {
|
||||
Set<String> attachmentNames = attachments == null ? Collections.<String>emptySet() : attachments.keySet();
|
||||
try (InputStream inputStream = summaryFile.getInputStream()) {
|
||||
return parse(fileName, inputStream, attachmentNames);
|
||||
} catch (IOException exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:读取台账汇总excel失败");
|
||||
}
|
||||
}
|
||||
|
||||
public ParsedLedger parse(String fileName, InputStream inputStream, Set<String> attachmentNames) {
|
||||
ParsedLedger ledger = parseForValidate(fileName, inputStream);
|
||||
ensureRequiredSheetsPresent(ledger);
|
||||
validateReferencedAttachments(ledger.getPoints(), attachmentNames);
|
||||
return ledger;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
try (InputStream inputStream = summaryFile.getInputStream();
|
||||
Workbook workbook = WorkbookFactory.create(inputStream)) {
|
||||
try (Workbook workbook = WorkbookFactory.create(inputStream)) {
|
||||
ParsedLedger ledger = new ParsedLedger();
|
||||
ledger.setSummaryFileName(fileName);
|
||||
ledger.setPoints(parsePointSheet(requireSheet(workbook, TestReportConst.LEDGER_POINT_SHEET_NAME), attachments));
|
||||
ledger.setDevices(parseDeviceSheet(workbook.getSheet(TestReportConst.LEDGER_DEVICE_SHEET_NAME)));
|
||||
ledger.setClients(parseClientSheet(workbook.getSheet(TestReportConst.LEDGER_CLIENT_SHEET_NAME)));
|
||||
validateReferencedAttachments(ledger.getPoints(), attachments);
|
||||
ledger.setSummaryFileName(TestReportConst.LEDGER_SUMMARY_FILE_NAME);
|
||||
ledger.getRequiredSheets().addAll(REQUIRED_SHEETS);
|
||||
collectSheets(ledger, workbook);
|
||||
|
||||
Sheet pointSheet = workbook.getSheet(TestReportConst.LEDGER_POINT_SHEET_NAME);
|
||||
if (pointSheet != null) {
|
||||
ledger.setPoints(parsePointSheet(pointSheet));
|
||||
}
|
||||
Sheet deviceSheet = workbook.getSheet(TestReportConst.LEDGER_DEVICE_SHEET_NAME);
|
||||
if (deviceSheet != null) {
|
||||
ledger.setDevices(parseDeviceSheet(deviceSheet));
|
||||
}
|
||||
Sheet clientSheet = workbook.getSheet(TestReportConst.LEDGER_CLIENT_SHEET_NAME);
|
||||
if (clientSheet != null) {
|
||||
ledger.setClients(parseClientSheet(clientSheet));
|
||||
}
|
||||
return ledger;
|
||||
} catch (BusinessException exception) {
|
||||
throw exception;
|
||||
@@ -63,15 +95,25 @@ public class TestReportLedgerExcelParser {
|
||||
}
|
||||
}
|
||||
|
||||
private Sheet requireSheet(Workbook workbook, String sheetName) {
|
||||
Sheet sheet = workbook.getSheet(sheetName);
|
||||
if (sheet == null) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:缺少" + sheetName + "sheet");
|
||||
private void collectSheets(ParsedLedger ledger, Workbook workbook) {
|
||||
for (String sheetName : REQUIRED_SHEETS) {
|
||||
Sheet sheet = workbook.getSheet(sheetName);
|
||||
if (sheet == null) {
|
||||
ledger.getMissingSheets().add(sheetName);
|
||||
continue;
|
||||
}
|
||||
ledger.getExistingSheets().add(sheetName);
|
||||
}
|
||||
return sheet;
|
||||
}
|
||||
|
||||
private List<ParsedPointRow> parsePointSheet(Sheet sheet, Map<String, MultipartFile> attachments) {
|
||||
private void ensureRequiredSheetsPresent(ParsedLedger ledger) {
|
||||
if (!ledger.getMissingSheets().isEmpty()) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL,
|
||||
"校验文件阶段:缺少" + String.join("、", ledger.getMissingSheets()) + "sheet");
|
||||
}
|
||||
}
|
||||
|
||||
private List<ParsedPointRow> parsePointSheet(Sheet sheet) {
|
||||
validateHeaders(sheet, POINT_HEADERS, "监测点信息");
|
||||
List<ParsedPointRow> points = new ArrayList<ParsedPointRow>();
|
||||
Set<String> pointKeys = new HashSet<String>();
|
||||
@@ -95,12 +137,6 @@ public class TestReportLedgerExcelParser {
|
||||
"第" + (rowIndex + 1) + "行Excel附件扩展名仅允许xls/xlsx");
|
||||
validateExtension(point.getWordAttachmentName(), Arrays.asList(".doc", ".docx"),
|
||||
"第" + (rowIndex + 1) + "行Word附件扩展名仅允许doc/docx");
|
||||
if (!attachments.containsKey(point.getExcelAttachmentName())) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:第" + (rowIndex + 1) + "行缺少Excel附件");
|
||||
}
|
||||
if (!attachments.containsKey(point.getWordAttachmentName())) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:第" + (rowIndex + 1) + "行缺少Word附件");
|
||||
}
|
||||
String pointKey = point.getSubstationName() + "|" + point.getPointName();
|
||||
if (!pointKeys.add(pointKey)) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:监测点信息中存在重复点位");
|
||||
@@ -120,9 +156,6 @@ public class TestReportLedgerExcelParser {
|
||||
}
|
||||
|
||||
private List<ParsedDeviceRow> parseDeviceSheet(Sheet sheet) {
|
||||
if (sheet == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
validateHeaders(sheet, DEVICE_HEADERS, "检测设备");
|
||||
List<ParsedDeviceRow> devices = new ArrayList<ParsedDeviceRow>();
|
||||
Set<String> deviceNos = new HashSet<String>();
|
||||
@@ -147,9 +180,6 @@ public class TestReportLedgerExcelParser {
|
||||
}
|
||||
|
||||
private List<ParsedClientRow> parseClientSheet(Sheet sheet) {
|
||||
if (sheet == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
validateHeaders(sheet, CLIENT_HEADERS, "委托单位");
|
||||
List<ParsedClientRow> clients = new ArrayList<ParsedClientRow>();
|
||||
Set<String> clientNames = new HashSet<String>();
|
||||
@@ -172,13 +202,25 @@ public class TestReportLedgerExcelParser {
|
||||
return clients;
|
||||
}
|
||||
|
||||
private void validateReferencedAttachments(List<ParsedPointRow> points, Map<String, MultipartFile> attachments) {
|
||||
private void validateReferencedAttachments(List<ParsedPointRow> points, Set<String> attachmentNames) {
|
||||
Set<String> referencedNames = new HashSet<String>();
|
||||
for (ParsedPointRow point : points) {
|
||||
referencedNames.add(point.getExcelAttachmentName());
|
||||
referencedNames.add(point.getWordAttachmentName());
|
||||
if (StrUtil.isNotBlank(point.getExcelAttachmentName())) {
|
||||
referencedNames.add(point.getExcelAttachmentName());
|
||||
}
|
||||
if (StrUtil.isNotBlank(point.getWordAttachmentName())) {
|
||||
referencedNames.add(point.getWordAttachmentName());
|
||||
}
|
||||
}
|
||||
for (String attachmentName : attachments.keySet()) {
|
||||
for (ParsedPointRow point : points) {
|
||||
if (!attachmentNames.contains(point.getExcelAttachmentName())) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:第" + point.getRowNo() + "行缺少Excel附件");
|
||||
}
|
||||
if (!attachmentNames.contains(point.getWordAttachmentName())) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:第" + point.getRowNo() + "行缺少Word附件");
|
||||
}
|
||||
}
|
||||
for (String attachmentName : attachmentNames) {
|
||||
if (!referencedNames.contains(attachmentName)) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:存在未在监测点信息sheet中引用的附件 " + attachmentName);
|
||||
}
|
||||
@@ -263,6 +305,9 @@ public class TestReportLedgerExcelParser {
|
||||
@Data
|
||||
public static class ParsedLedger {
|
||||
private String summaryFileName;
|
||||
private List<String> requiredSheets = new ArrayList<String>();
|
||||
private List<String> existingSheets = new ArrayList<String>();
|
||||
private List<String> missingSheets = new ArrayList<String>();
|
||||
private List<ParsedPointRow> points = new ArrayList<ParsedPointRow>();
|
||||
private List<ParsedDeviceRow> devices = new ArrayList<ParsedDeviceRow>();
|
||||
private List<ParsedClientRow> clients = new ArrayList<ParsedClientRow>();
|
||||
|
||||
@@ -23,9 +23,14 @@ import com.njcn.gather.aireport.testreport.pojo.po.TestReportPO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.po.TestReportPointPO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportLedgerImportResultVO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportLedgerImportStageVO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportLedgerValidateAttachmentVO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportLedgerValidatePointVO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportLedgerValidateResultVO;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -42,6 +47,7 @@ import java.util.stream.Collectors;
|
||||
public class TestReportLedgerImportService {
|
||||
|
||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
private static final String BATCH_STATUS_VALIDATED = "validated";
|
||||
|
||||
private final TestReportMapper testReportMapper;
|
||||
private final TestReportPointMapper testReportPointMapper;
|
||||
@@ -67,36 +73,315 @@ public class TestReportLedgerImportService {
|
||||
this.testReportStorageService = testReportStorageService;
|
||||
}
|
||||
|
||||
public TestReportLedgerImportResultVO importLedger(TestReportPO report, MultipartFile[] files, String userId) {
|
||||
public TestReportLedgerValidateResultVO validateLedgerSession(String sessionId, MultipartFile[] files) {
|
||||
Map<String, MultipartFile> fileMap = normalizeFileMap(files);
|
||||
MultipartFile summaryFile = fileMap.remove(TestReportConst.LEDGER_SUMMARY_FILE_NAME);
|
||||
if (summaryFile == null) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:缺少台账信息汇总.xlsx");
|
||||
TestReportStorageService.UploadedBatch uploadedBatch = testReportStorageService.saveValidateSessionFiles(sessionId, fileMap);
|
||||
List<String> sessionFileNames = testReportStorageService.listValidateSessionFileNames(sessionId);
|
||||
Set<String> sessionFileNameSet = new HashSet<String>(sessionFileNames);
|
||||
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));
|
||||
if (!Boolean.TRUE.equals(result.getSummaryFilePresent())) {
|
||||
result.setSuccess(Boolean.FALSE);
|
||||
result.getFailReasons().add("鏍¢獙鏂囦欢闃舵锛氱己灏戝彴璐︿俊鎭眹鎬?xlsx");
|
||||
persistValidateSessionResult(sessionId, result);
|
||||
return result;
|
||||
}
|
||||
TestReportLedgerExcelParser.ParsedLedger ledger = testReportLedgerExcelParser.parse(summaryFile, fileMap);
|
||||
try (InputStream inputStream = Files.newInputStream(
|
||||
testReportStorageService.resolveValidateSessionFile(sessionId, TestReportConst.LEDGER_SUMMARY_FILE_NAME))) {
|
||||
TestReportLedgerExcelParser.ParsedLedger ledger = testReportLedgerExcelParser.parseForValidate(
|
||||
TestReportConst.LEDGER_SUMMARY_FILE_NAME, inputStream);
|
||||
Map<String, TestDevicePO> dbDeviceMap = loadExistingDevices(collectDeviceNos(ledger));
|
||||
Map<String, ClientUnitPO> dbClientMap = loadExistingClients(collectClientNames(ledger));
|
||||
result.getRequiredSheets().addAll(ledger.getRequiredSheets());
|
||||
result.getExistingSheets().addAll(ledger.getExistingSheets());
|
||||
result.getMissingSheets().addAll(ledger.getMissingSheets());
|
||||
fillPointValidationResult(result, ledger, sessionFileNameSet, dbDeviceMap, dbClientMap);
|
||||
result.getOrphanAttachmentNames().addAll(resolveOrphanAttachmentNames(ledger, sessionFileNameSet));
|
||||
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()));
|
||||
}
|
||||
if (result.getMissingAttachmentCount() != null && result.getMissingAttachmentCount() > 0) {
|
||||
result.getFailReasons().add("鏍¢獙鏂囦欢闃舵锛氬瓨鍦ㄧ己澶遍檮浠讹紝鍙户缁笂浼犲悗鍐嶆鏍¢獙");
|
||||
}
|
||||
appendReferenceFailReasons(result);
|
||||
persistValidateSessionResult(sessionId, result);
|
||||
return result;
|
||||
} catch (BusinessException exception) {
|
||||
throw exception;
|
||||
} catch (Exception exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "鏍¢獙鏂囦欢闃舵锛氳鍙栧彴璐︽眹鎬籩xcel澶辫触");
|
||||
}
|
||||
}
|
||||
|
||||
public TestReportLedgerImportResultVO validateLedger(TestReportPO report, MultipartFile[] files, String userId) {
|
||||
Map<String, MultipartFile> fileMap = normalizeFileMap(files);
|
||||
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");
|
||||
}
|
||||
TestReportLedgerExcelParser.ParsedLedger ledger = parseLedgerFromBatch(report.getId(), batchId, fileMap.keySet());
|
||||
Map<String, TestDevicePO> dbDeviceMap = loadExistingDevices(collectDeviceNos(ledger));
|
||||
Map<String, ClientUnitPO> dbClientMap = loadExistingClients(collectClientNames(ledger));
|
||||
validatePointReferences(ledger.getPoints(), dbDeviceMap, dbClientMap, ledger.toDeviceMap(), ledger.toClientMap());
|
||||
writeValidatedBatchMeta(report.getId(), batchId, uploadedBatch, ledger, userId);
|
||||
return buildValidateSuccessResult(batchId, uploadedBatch.getTempStoragePath(), ledger, fileMap.size());
|
||||
} catch (RuntimeException exception) {
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
public TestReportLedgerImportResultVO importLedger(TestReportPO report, String batchId, String userId) {
|
||||
BatchMeta batchMeta = loadValidatedBatchMeta(report.getId(), batchId);
|
||||
TestReportLedgerExcelParser.ParsedLedger ledger = parseLedgerFromBatch(report.getId(), batchId, batchMeta.toFileNameSet());
|
||||
|
||||
Map<String, TestDevicePO> dbDeviceMap = loadExistingDevices(collectDeviceNos(ledger));
|
||||
Map<String, ClientUnitPO> dbClientMap = loadExistingClients(collectClientNames(ledger));
|
||||
Map<String, TestReportLedgerExcelParser.ParsedDeviceRow> sheetDeviceMap = ledger.toDeviceMap();
|
||||
Map<String, TestReportLedgerExcelParser.ParsedClientRow> sheetClientMap = ledger.toClientMap();
|
||||
validatePointReferences(ledger.getPoints(), dbDeviceMap, dbClientMap, sheetDeviceMap, sheetClientMap);
|
||||
|
||||
BaseDataSummary baseDataSummary = maintainBaseData(ledger, dbDeviceMap, dbClientMap, userId);
|
||||
|
||||
List<TestReportPointPO> oldPoints = loadNormalPoints(report.getId());
|
||||
List<TestReportGroupReportPO> oldGroupReports = loadNormalGroupReports(report.getId());
|
||||
deleteOldFiles(oldPoints, oldGroupReports, report.getData());
|
||||
removeOldRows(report.getId());
|
||||
|
||||
String summaryStoragePath = testReportStorageService.saveLedgerSummaryExcel(report.getId(), summaryFile);
|
||||
int groupCount = saveNewPoints(report.getId(), ledger.getPoints(), fileMap, userId);
|
||||
String summaryStoragePath = testReportStorageService.saveLedgerSummaryFromTemp(report.getId(), batchId,
|
||||
batchMeta.getSummaryFileName());
|
||||
int groupCount = saveNewPoints(report.getId(), batchId, ledger.getPoints(), userId);
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
report.setData(buildSnapshotJson(ledger, summaryStoragePath, baseDataSummary, groupCount));
|
||||
report.setReportGenerateState(TestReportConst.REPORT_GENERATE_STATE_PENDING);
|
||||
report.setUpdateBy(userId);
|
||||
report.setUpdateTime(now);
|
||||
testReportMapper.updateById(report);
|
||||
testReportStorageService.deleteTempBatchQuietly(report.getId(), batchId);
|
||||
return buildImportSuccessResult(batchId, batchMeta.getTempStoragePath(), ledger, baseDataSummary,
|
||||
batchMeta.getUploadedFileCount(), groupCount);
|
||||
}
|
||||
|
||||
return buildSuccessResult(ledger, baseDataSummary, fileMap.size() + 1, groupCount);
|
||||
private TestReportLedgerExcelParser.ParsedLedger parseLedgerFromBatch(String reportId, String batchId, Collection<String> fileNames) {
|
||||
try (InputStream inputStream = java.nio.file.Files.newInputStream(
|
||||
testReportStorageService.resolveTempBatchFile(reportId, batchId, TestReportConst.LEDGER_SUMMARY_FILE_NAME))) {
|
||||
return testReportLedgerExcelParser.parse(TestReportConst.LEDGER_SUMMARY_FILE_NAME, inputStream,
|
||||
buildAttachmentNameSet(fileNames));
|
||||
} catch (java.io.IOException exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:读取台账汇总excel失败");
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> buildAttachmentNameSet(Collection<String> fileNames) {
|
||||
Set<String> attachmentNames = new HashSet<String>();
|
||||
if (fileNames == null || fileNames.isEmpty()) {
|
||||
return attachmentNames;
|
||||
}
|
||||
for (String fileName : fileNames) {
|
||||
if (StrUtil.isBlank(fileName) || TestReportConst.LEDGER_SUMMARY_FILE_NAME.equals(fileName)) {
|
||||
continue;
|
||||
}
|
||||
attachmentNames.add(fileName);
|
||||
}
|
||||
return attachmentNames;
|
||||
}
|
||||
|
||||
private void fillPointValidationResult(TestReportLedgerValidateResultVO result,
|
||||
TestReportLedgerExcelParser.ParsedLedger ledger,
|
||||
Set<String> sessionFileNameSet,
|
||||
Map<String, TestDevicePO> dbDeviceMap,
|
||||
Map<String, ClientUnitPO> dbClientMap) {
|
||||
int missingAttachmentCount = 0;
|
||||
Map<String, TestReportLedgerExcelParser.ParsedDeviceRow> sheetDeviceMap = ledger.toDeviceMap();
|
||||
Map<String, TestReportLedgerExcelParser.ParsedClientRow> sheetClientMap = ledger.toClientMap();
|
||||
for (TestReportLedgerExcelParser.ParsedPointRow pointRow : ledger.getPoints()) {
|
||||
TestReportLedgerValidatePointVO pointVO = new TestReportLedgerValidatePointVO();
|
||||
pointVO.setRowNo(pointRow.getRowNo());
|
||||
pointVO.setSubstationName(pointRow.getSubstationName());
|
||||
pointVO.setPointName(pointRow.getPointName());
|
||||
pointVO.setPointKey(pointRow.getSubstationName() + "|" + pointRow.getPointName());
|
||||
|
||||
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 (Boolean.TRUE.equals(wordAttachment.getMissing())) {
|
||||
pointMissingCount++;
|
||||
}
|
||||
pointVO.setMissingAttachmentCount(pointMissingCount);
|
||||
fillReferenceValidationResult(pointVO, pointRow, dbDeviceMap, dbClientMap, sheetDeviceMap, sheetClientMap);
|
||||
pointVO.setComplete(pointMissingCount == 0
|
||||
&& !Boolean.FALSE.equals(pointVO.getTestDeviceNoValid())
|
||||
&& !Boolean.FALSE.equals(pointVO.getClientUnitNameValid()));
|
||||
missingAttachmentCount += pointMissingCount;
|
||||
result.getPoints().add(pointVO);
|
||||
}
|
||||
result.setPointCount(result.getPoints().size());
|
||||
result.setMissingAttachmentCount(missingAttachmentCount);
|
||||
}
|
||||
|
||||
private void fillReferenceValidationResult(TestReportLedgerValidatePointVO pointVO,
|
||||
TestReportLedgerExcelParser.ParsedPointRow pointRow,
|
||||
Map<String, TestDevicePO> dbDeviceMap,
|
||||
Map<String, ClientUnitPO> dbClientMap,
|
||||
Map<String, TestReportLedgerExcelParser.ParsedDeviceRow> sheetDeviceMap,
|
||||
Map<String, TestReportLedgerExcelParser.ParsedClientRow> sheetClientMap) {
|
||||
if (StrUtil.isBlank(pointRow.getTestDeviceNo())) {
|
||||
pointVO.setTestDeviceNoValid(Boolean.TRUE);
|
||||
pointVO.setTestDeviceNoMessage("检测设备编号为空,跳过校验");
|
||||
} else if (dbDeviceMap.containsKey(pointRow.getTestDeviceNo()) || sheetDeviceMap.containsKey(pointRow.getTestDeviceNo())) {
|
||||
pointVO.setTestDeviceNoValid(Boolean.TRUE);
|
||||
pointVO.setTestDeviceNoMessage("检测设备编号校验通过");
|
||||
} else {
|
||||
pointVO.setTestDeviceNoValid(Boolean.FALSE);
|
||||
pointVO.setTestDeviceNoMessage("第" + pointRow.getRowNo() + "行检测设备编号在数据库和检测设备sheet中都不存在");
|
||||
}
|
||||
if (StrUtil.isBlank(pointRow.getClientUnitName())) {
|
||||
pointVO.setClientUnitNameValid(Boolean.TRUE);
|
||||
pointVO.setClientUnitNameMessage("委托单位名称为空,跳过校验");
|
||||
} else if (dbClientMap.containsKey(pointRow.getClientUnitName()) || sheetClientMap.containsKey(pointRow.getClientUnitName())) {
|
||||
pointVO.setClientUnitNameValid(Boolean.TRUE);
|
||||
pointVO.setClientUnitNameMessage("委托单位名称校验通过");
|
||||
} else {
|
||||
pointVO.setClientUnitNameValid(Boolean.FALSE);
|
||||
pointVO.setClientUnitNameMessage("第" + pointRow.getRowNo() + "行委托单位名称在数据库和委托单位sheet中都不存在");
|
||||
}
|
||||
}
|
||||
|
||||
private TestReportLedgerValidateAttachmentVO buildAttachmentRow(String attachmentName, String attachmentType,
|
||||
boolean uploaded) {
|
||||
TestReportLedgerValidateAttachmentVO attachmentVO = new TestReportLedgerValidateAttachmentVO();
|
||||
attachmentVO.setAttachmentName(attachmentName);
|
||||
attachmentVO.setAttachmentType(attachmentType);
|
||||
attachmentVO.setReferenced(Boolean.TRUE);
|
||||
attachmentVO.setUploaded(uploaded);
|
||||
attachmentVO.setMissing(!uploaded);
|
||||
attachmentVO.setReuploadAllowed(Boolean.TRUE);
|
||||
attachmentVO.setMessage(uploaded ? "宸蹭笂浼?" : "缂哄皯闄勪欢锛屽彲缁х画涓婁紶");
|
||||
return attachmentVO;
|
||||
}
|
||||
|
||||
private List<String> resolveOrphanAttachmentNames(TestReportLedgerExcelParser.ParsedLedger ledger, Set<String> sessionFileNameSet) {
|
||||
Set<String> referencedNames = new HashSet<String>();
|
||||
for (TestReportLedgerExcelParser.ParsedPointRow pointRow : ledger.getPoints()) {
|
||||
if (StrUtil.isNotBlank(pointRow.getExcelAttachmentName())) {
|
||||
referencedNames.add(pointRow.getExcelAttachmentName());
|
||||
}
|
||||
if (StrUtil.isNotBlank(pointRow.getWordAttachmentName())) {
|
||||
referencedNames.add(pointRow.getWordAttachmentName());
|
||||
}
|
||||
}
|
||||
List<String> orphanAttachmentNames = new java.util.ArrayList<String>();
|
||||
for (String fileName : sessionFileNameSet) {
|
||||
if (TestReportConst.LEDGER_SUMMARY_FILE_NAME.equals(fileName)) {
|
||||
continue;
|
||||
}
|
||||
if (!referencedNames.contains(fileName)) {
|
||||
orphanAttachmentNames.add(fileName);
|
||||
}
|
||||
}
|
||||
return orphanAttachmentNames;
|
||||
}
|
||||
|
||||
private void persistValidateSessionResult(String sessionId, TestReportLedgerValidateResultVO result) {
|
||||
try {
|
||||
testReportStorageService.writeValidateSessionResult(sessionId, OBJECT_MAPPER.writeValueAsString(result));
|
||||
} catch (Exception exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "鏍¢獙鏂囦欢闃舵锛氫繚瀛樻牎楠岀粨鏋滃け璐?");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasReferenceErrors(List<TestReportLedgerValidatePointVO> points) {
|
||||
if (points == null || points.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
for (TestReportLedgerValidatePointVO point : points) {
|
||||
if (point == null) {
|
||||
continue;
|
||||
}
|
||||
if (Boolean.FALSE.equals(point.getTestDeviceNoValid()) || Boolean.FALSE.equals(point.getClientUnitNameValid())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void appendReferenceFailReasons(TestReportLedgerValidateResultVO result) {
|
||||
if (result == null || result.getPoints() == null || result.getPoints().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
boolean hasDeviceError = false;
|
||||
boolean hasClientError = false;
|
||||
for (TestReportLedgerValidatePointVO point : result.getPoints()) {
|
||||
if (point == null) {
|
||||
continue;
|
||||
}
|
||||
if (Boolean.FALSE.equals(point.getTestDeviceNoValid())) {
|
||||
hasDeviceError = true;
|
||||
}
|
||||
if (Boolean.FALSE.equals(point.getClientUnitNameValid())) {
|
||||
hasClientError = true;
|
||||
}
|
||||
}
|
||||
if (hasDeviceError) {
|
||||
result.getFailReasons().add("监测点信息中存在检测设备编号未匹配到数据库或检测设备sheet的记录");
|
||||
}
|
||||
if (hasClientError) {
|
||||
result.getFailReasons().add("监测点信息中存在委托单位名称未匹配到数据库或委托单位sheet的记录");
|
||||
}
|
||||
}
|
||||
|
||||
private void writeValidatedBatchMeta(String reportId, String batchId, TestReportStorageService.UploadedBatch uploadedBatch,
|
||||
TestReportLedgerExcelParser.ParsedLedger ledger, String userId) {
|
||||
BatchMeta batchMeta = new BatchMeta();
|
||||
batchMeta.setBatchId(batchId);
|
||||
batchMeta.setReportId(reportId);
|
||||
batchMeta.setStatus(BATCH_STATUS_VALIDATED);
|
||||
batchMeta.setSummaryFileName(ledger.getSummaryFileName());
|
||||
batchMeta.setTempStoragePath(uploadedBatch.getTempStoragePath());
|
||||
batchMeta.setUploadedFileCount(uploadedBatch.getStoredFileMap().size());
|
||||
batchMeta.setUploadedFileNames(uploadedBatch.getStoredFileMap().keySet().stream().collect(Collectors.toList()));
|
||||
batchMeta.setValidatedBy(userId);
|
||||
batchMeta.setValidatedTime(LocalDateTime.now().toString());
|
||||
try {
|
||||
testReportStorageService.writeTempBatchMeta(reportId, batchId, OBJECT_MAPPER.writeValueAsString(batchMeta));
|
||||
} catch (Exception exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:保存批次元数据失败");
|
||||
}
|
||||
}
|
||||
|
||||
private BatchMeta loadValidatedBatchMeta(String reportId, String batchId) {
|
||||
if (StrUtil.isBlank(batchId)) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "导入批次ID不能为空");
|
||||
}
|
||||
String metaText = testReportStorageService.readTempBatchMeta(reportId, batchId);
|
||||
if (StrUtil.isBlank(metaText)) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "导入批次不存在或未通过校验");
|
||||
}
|
||||
try {
|
||||
BatchMeta batchMeta = OBJECT_MAPPER.readValue(metaText, BatchMeta.class);
|
||||
if (!reportId.equals(batchMeta.getReportId()) || !BATCH_STATUS_VALIDATED.equals(batchMeta.getStatus())) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "导入批次不存在或未通过校验");
|
||||
}
|
||||
return batchMeta;
|
||||
} catch (BusinessException exception) {
|
||||
throw exception;
|
||||
} catch (Exception exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "读取导入批次信息失败");
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, MultipartFile> normalizeFileMap(MultipartFile[] files) {
|
||||
@@ -160,7 +445,7 @@ public class TestReportLedgerImportService {
|
||||
String typeId = typeNameIdMap.get(deviceRow.getTypeName());
|
||||
if (StrUtil.isBlank(typeId)) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL,
|
||||
"基础资料维护阶段:检测设备sheet第" + deviceRow.getRowNo() + "行设备型号不存在");
|
||||
"基础信息维护阶段:检测设备sheet第" + deviceRow.getRowNo() + "行设备型号不存在");
|
||||
}
|
||||
TestDevicePO device = new TestDevicePO();
|
||||
device.setId(UUID.randomUUID().toString());
|
||||
@@ -203,14 +488,12 @@ public class TestReportLedgerImportService {
|
||||
return summary;
|
||||
}
|
||||
|
||||
private int saveNewPoints(String reportId, List<TestReportLedgerExcelParser.ParsedPointRow> points,
|
||||
Map<String, MultipartFile> fileMap, String userId) {
|
||||
private int saveNewPoints(String reportId, String batchId, List<TestReportLedgerExcelParser.ParsedPointRow> points,
|
||||
String userId) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
Set<Integer> groupNos = new HashSet<Integer>();
|
||||
int sortNo = 1;
|
||||
for (TestReportLedgerExcelParser.ParsedPointRow pointRow : points) {
|
||||
MultipartFile excelFile = fileMap.get(pointRow.getExcelAttachmentName());
|
||||
MultipartFile wordFile = fileMap.get(pointRow.getWordAttachmentName());
|
||||
TestReportPointPO point = new TestReportPointPO();
|
||||
point.setId(UUID.randomUUID().toString());
|
||||
point.setReportId(reportId);
|
||||
@@ -220,9 +503,11 @@ public class TestReportLedgerImportService {
|
||||
point.setTestDeviceNo(pointRow.getTestDeviceNo());
|
||||
point.setClientUnitName(pointRow.getClientUnitName());
|
||||
point.setExcelAttachmentName(pointRow.getExcelAttachmentName());
|
||||
point.setExcelStoragePath(testReportStorageService.savePointExcelAttachment(reportId, excelFile));
|
||||
point.setExcelStoragePath(testReportStorageService.savePointExcelFromTemp(reportId, batchId,
|
||||
pointRow.getExcelAttachmentName()));
|
||||
point.setWordAttachmentName(pointRow.getWordAttachmentName());
|
||||
point.setWordStoragePath(testReportStorageService.savePointWordAttachment(reportId, wordFile));
|
||||
point.setWordStoragePath(testReportStorageService.savePointWordFromTemp(reportId, batchId,
|
||||
pointRow.getWordAttachmentName()));
|
||||
point.setSortNo(sortNo++);
|
||||
point.setDeleted(TestReportConst.DELETED_NO);
|
||||
point.setCreateBy(userId);
|
||||
@@ -297,13 +582,37 @@ public class TestReportLedgerImportService {
|
||||
return root.toString();
|
||||
}
|
||||
|
||||
private TestReportLedgerImportResultVO buildSuccessResult(TestReportLedgerExcelParser.ParsedLedger ledger,
|
||||
BaseDataSummary summary, int totalFileCount, int groupCount) {
|
||||
private TestReportLedgerImportResultVO buildValidateSuccessResult(String batchId, String tempStoragePath,
|
||||
TestReportLedgerExcelParser.ParsedLedger ledger,
|
||||
int uploadedFileCount) {
|
||||
TestReportLedgerImportResultVO result = new TestReportLedgerImportResultVO();
|
||||
result.setBatchId(batchId);
|
||||
result.setCurrentStage("文件校验");
|
||||
result.setSuccess(Boolean.TRUE);
|
||||
result.setSummaryFileName(ledger.getSummaryFileName());
|
||||
result.setTempStoragePath(tempStoragePath);
|
||||
result.setUploadedFileCount(uploadedFileCount);
|
||||
result.setTotalFileCount(uploadedFileCount);
|
||||
result.setPointCount(ledger.getPoints().size());
|
||||
result.setExcelAttachmentCount(ledger.getPoints().size());
|
||||
result.setWordAttachmentCount(ledger.getPoints().size());
|
||||
result.getStages().add(buildStage("文件选择", "文件选择完成"));
|
||||
result.getStages().add(buildStage("文件校验", "文件校验通过"));
|
||||
return result;
|
||||
}
|
||||
|
||||
private TestReportLedgerImportResultVO buildImportSuccessResult(String batchId, String tempStoragePath,
|
||||
TestReportLedgerExcelParser.ParsedLedger ledger,
|
||||
BaseDataSummary summary, int uploadedFileCount,
|
||||
int groupCount) {
|
||||
TestReportLedgerImportResultVO result = new TestReportLedgerImportResultVO();
|
||||
result.setBatchId(batchId);
|
||||
result.setCurrentStage("完成");
|
||||
result.setSuccess(Boolean.TRUE);
|
||||
result.setSummaryFileName(ledger.getSummaryFileName());
|
||||
result.setTotalFileCount(totalFileCount);
|
||||
result.setTempStoragePath(tempStoragePath);
|
||||
result.setUploadedFileCount(uploadedFileCount);
|
||||
result.setTotalFileCount(uploadedFileCount);
|
||||
result.setPointCount(ledger.getPoints().size());
|
||||
result.setExcelAttachmentCount(ledger.getPoints().size());
|
||||
result.setWordAttachmentCount(ledger.getPoints().size());
|
||||
@@ -312,10 +621,10 @@ public class TestReportLedgerImportService {
|
||||
result.setClientAddedCount(summary.getClientAddedCount());
|
||||
result.setClientReuseCount(summary.getClientReuseCount());
|
||||
result.setGroupCount(groupCount);
|
||||
result.getStages().add(buildStage("选择文件", "文件选择完成"));
|
||||
result.getStages().add(buildStage("校验文件", "文件校验通过"));
|
||||
result.getStages().add(buildStage("文件上传", "文件上传完成"));
|
||||
result.getStages().add(buildStage("基础资料维护", "基础资料维护完成"));
|
||||
result.getStages().add(buildStage("文件选择", "文件选择完成"));
|
||||
result.getStages().add(buildStage("文件校验", "已复用已校验批次"));
|
||||
result.getStages().add(buildStage("文件导入", "正式文件和点位数据已导入"));
|
||||
result.getStages().add(buildStage("基础信息维护", "基础信息维护完成"));
|
||||
result.getStages().add(buildStage("完成", "导入完成"));
|
||||
return result;
|
||||
}
|
||||
@@ -425,7 +734,7 @@ public class TestReportLedgerImportService {
|
||||
return TestDeviceConst.STATE_RETIRED;
|
||||
}
|
||||
throw new BusinessException(CommonResponseEnum.FAIL,
|
||||
"基础资料维护阶段:检测设备sheet第" + rowNo + "行设备状态不正确");
|
||||
"基础信息维护阶段:检测设备sheet第" + rowNo + "行设备状态不正确");
|
||||
}
|
||||
|
||||
private String defaultEmpty(String value) {
|
||||
@@ -433,7 +742,8 @@ public class TestReportLedgerImportService {
|
||||
}
|
||||
|
||||
private String normalizeFileName(String fileName) {
|
||||
return fileName == null ? null : fileName.trim();
|
||||
String normalized = fileName == null ? null : fileName.trim();
|
||||
return StrUtil.isBlank(normalized) ? null : normalized;
|
||||
}
|
||||
|
||||
private static class BaseDataSummary {
|
||||
@@ -474,4 +784,92 @@ public class TestReportLedgerImportService {
|
||||
this.clientReuseCount = clientReuseCount;
|
||||
}
|
||||
}
|
||||
|
||||
public static class BatchMeta {
|
||||
private String batchId;
|
||||
private String reportId;
|
||||
private String status;
|
||||
private String summaryFileName;
|
||||
private String tempStoragePath;
|
||||
private Integer uploadedFileCount;
|
||||
private List<String> uploadedFileNames;
|
||||
private String validatedBy;
|
||||
private String validatedTime;
|
||||
|
||||
public Set<String> toFileNameSet() {
|
||||
return uploadedFileNames == null ? Collections.<String>emptySet() : new HashSet<String>(uploadedFileNames);
|
||||
}
|
||||
|
||||
public String getBatchId() {
|
||||
return batchId;
|
||||
}
|
||||
|
||||
public void setBatchId(String batchId) {
|
||||
this.batchId = batchId;
|
||||
}
|
||||
|
||||
public String getReportId() {
|
||||
return reportId;
|
||||
}
|
||||
|
||||
public void setReportId(String reportId) {
|
||||
this.reportId = reportId;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getSummaryFileName() {
|
||||
return summaryFileName;
|
||||
}
|
||||
|
||||
public void setSummaryFileName(String summaryFileName) {
|
||||
this.summaryFileName = summaryFileName;
|
||||
}
|
||||
|
||||
public String getTempStoragePath() {
|
||||
return tempStoragePath;
|
||||
}
|
||||
|
||||
public void setTempStoragePath(String tempStoragePath) {
|
||||
this.tempStoragePath = tempStoragePath;
|
||||
}
|
||||
|
||||
public Integer getUploadedFileCount() {
|
||||
return uploadedFileCount;
|
||||
}
|
||||
|
||||
public void setUploadedFileCount(Integer uploadedFileCount) {
|
||||
this.uploadedFileCount = uploadedFileCount;
|
||||
}
|
||||
|
||||
public List<String> getUploadedFileNames() {
|
||||
return uploadedFileNames;
|
||||
}
|
||||
|
||||
public void setUploadedFileNames(List<String> uploadedFileNames) {
|
||||
this.uploadedFileNames = uploadedFileNames;
|
||||
}
|
||||
|
||||
public String getValidatedBy() {
|
||||
return validatedBy;
|
||||
}
|
||||
|
||||
public void setValidatedBy(String validatedBy) {
|
||||
this.validatedBy = validatedBy;
|
||||
}
|
||||
|
||||
public String getValidatedTime() {
|
||||
return validatedTime;
|
||||
}
|
||||
|
||||
public void setValidatedTime(String validatedTime) {
|
||||
this.validatedTime = validatedTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,15 @@ import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Component
|
||||
@@ -21,35 +27,146 @@ public class TestReportStorageService {
|
||||
private String filesRootPath;
|
||||
|
||||
public String saveSourceExcel(String reportId, MultipartFile file) {
|
||||
return saveMultipartFile(reportId, TestReportConst.STORAGE_SOURCE_EXCEL_DIR, file, "保存原始Excel失败");
|
||||
return saveMultipartFile(reportId, TestReportConst.STORAGE_SOURCE_EXCEL_DIR, file, "淇濆瓨鍘熷Excel澶辫触");
|
||||
}
|
||||
|
||||
public String saveLedgerSummaryExcel(String reportId, MultipartFile file) {
|
||||
return saveMultipartFile(reportId, TestReportConst.STORAGE_LEDGER_SUMMARY_DIR, file, "保存台账汇总Excel失败");
|
||||
return saveMultipartFile(reportId, TestReportConst.STORAGE_LEDGER_SUMMARY_DIR, file, "淇濆瓨鍙拌处姹囨€籈xcel澶辫触");
|
||||
}
|
||||
|
||||
public String savePointExcelAttachment(String reportId, MultipartFile file) {
|
||||
return saveMultipartFile(reportId, TestReportConst.STORAGE_POINT_EXCEL_DIR, file, "保存监测点Excel附件失败");
|
||||
return saveMultipartFile(reportId, TestReportConst.STORAGE_POINT_EXCEL_DIR, file, "淇濆瓨鐩戞祴鐐笶xcel闄勪欢澶辫触");
|
||||
}
|
||||
|
||||
public String savePointWordAttachment(String reportId, MultipartFile file) {
|
||||
return saveMultipartFile(reportId, TestReportConst.STORAGE_POINT_WORD_DIR, file, "保存监测点Word附件失败");
|
||||
return saveMultipartFile(reportId, TestReportConst.STORAGE_POINT_WORD_DIR, file, "淇濆瓨鐩戞祴鐐筗ord闄勪欢澶辫触");
|
||||
}
|
||||
|
||||
private String saveMultipartFile(String reportId, String childDir, MultipartFile file, String errorMessage) {
|
||||
String originalFileName = file == null ? null : file.getOriginalFilename();
|
||||
if (file == null || file.isEmpty() || StrUtil.isBlank(originalFileName)) {
|
||||
public UploadedBatch saveTempBatchFiles(String reportId, String batchId, Map<String, MultipartFile> fileMap) {
|
||||
if (fileMap == null || fileMap.isEmpty()) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "文件不能为空");
|
||||
}
|
||||
String fileName = UUID.randomUUID().toString().replace("-", "") + "-" + Paths.get(originalFileName).getFileName();
|
||||
Path targetPath = resolveReportDir(reportId, childDir).resolve(fileName);
|
||||
try {
|
||||
Files.createDirectories(targetPath.getParent());
|
||||
file.transferTo(targetPath.toFile());
|
||||
} catch (IOException exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, errorMessage);
|
||||
Map<String, String> storedFileMap = new LinkedHashMap<String, String>();
|
||||
for (Map.Entry<String, MultipartFile> entry : fileMap.entrySet()) {
|
||||
String originalFileName = entry.getKey();
|
||||
MultipartFile file = entry.getValue();
|
||||
if (file == null || file.isEmpty() || StrUtil.isBlank(originalFileName)) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "文件不能为空");
|
||||
}
|
||||
String safeFileName = Paths.get(originalFileName).getFileName().toString();
|
||||
Path targetPath = resolveTempUploadDir(reportId, batchId).resolve(safeFileName);
|
||||
try {
|
||||
Files.createDirectories(targetPath.getParent());
|
||||
file.transferTo(targetPath.toFile());
|
||||
} catch (IOException exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "文件上传阶段:保存临时文件失败");
|
||||
}
|
||||
storedFileMap.put(originalFileName, buildTempUploadRelativePath(reportId, batchId, safeFileName));
|
||||
}
|
||||
return buildRelativePath(reportId, childDir, fileName);
|
||||
UploadedBatch uploadedBatch = new UploadedBatch();
|
||||
uploadedBatch.setBatchId(batchId);
|
||||
uploadedBatch.setTempStoragePath(buildTempBatchRelativePath(reportId, batchId));
|
||||
uploadedBatch.setStoredFileMap(storedFileMap);
|
||||
return uploadedBatch;
|
||||
}
|
||||
|
||||
public UploadedBatch saveValidateSessionFiles(String sessionId, Map<String, MultipartFile> fileMap) {
|
||||
if (fileMap == null || fileMap.isEmpty()) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "鏂囦欢涓嶈兘涓虹┖");
|
||||
}
|
||||
Map<String, String> storedFileMap = new LinkedHashMap<String, String>();
|
||||
for (Map.Entry<String, MultipartFile> entry : fileMap.entrySet()) {
|
||||
String originalFileName = entry.getKey();
|
||||
MultipartFile file = entry.getValue();
|
||||
if (file == null || file.isEmpty() || StrUtil.isBlank(originalFileName)) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "鏂囦欢涓嶈兘涓虹┖");
|
||||
}
|
||||
String safeFileName = Paths.get(originalFileName).getFileName().toString();
|
||||
Path targetPath = resolveValidateSessionUploadDir(sessionId).resolve(safeFileName);
|
||||
try {
|
||||
Files.createDirectories(targetPath.getParent());
|
||||
file.transferTo(targetPath.toFile());
|
||||
} catch (IOException exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "鏂囦欢涓婁紶闃舵锛氫繚瀛樻牎楠屼細璇濇枃浠跺け璐?");
|
||||
}
|
||||
storedFileMap.put(originalFileName, buildValidateSessionUploadRelativePath(sessionId, safeFileName));
|
||||
}
|
||||
UploadedBatch uploadedBatch = new UploadedBatch();
|
||||
uploadedBatch.setBatchId(sessionId);
|
||||
uploadedBatch.setTempStoragePath(buildValidateSessionRelativePath(sessionId));
|
||||
uploadedBatch.setStoredFileMap(storedFileMap);
|
||||
return uploadedBatch;
|
||||
}
|
||||
|
||||
public Path resolveTempBatchFile(String reportId, String batchId, String fileName) {
|
||||
return resolveTempUploadDir(reportId, batchId).resolve(Paths.get(fileName).getFileName().toString()).normalize();
|
||||
}
|
||||
|
||||
public void writeTempBatchMeta(String reportId, String batchId, String content) {
|
||||
Path metaPath = resolveTempBatchMetaPath(reportId, batchId);
|
||||
try {
|
||||
Files.createDirectories(metaPath.getParent());
|
||||
Files.write(metaPath, content.getBytes(StandardCharsets.UTF_8));
|
||||
} catch (IOException exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:保存批次元数据失败");
|
||||
}
|
||||
}
|
||||
|
||||
public void writeValidateSessionResult(String sessionId, String content) {
|
||||
Path resultPath = resolveValidateSessionResultPath(sessionId);
|
||||
try {
|
||||
Files.createDirectories(resultPath.getParent());
|
||||
Files.write(resultPath, content.getBytes(StandardCharsets.UTF_8));
|
||||
} catch (IOException exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "鏍¢獙鏂囦欢闃舵锛氫繚瀛樻牎楠岀粨鏋滃け璐?");
|
||||
}
|
||||
}
|
||||
|
||||
public Path resolveValidateSessionFile(String sessionId, String fileName) {
|
||||
return resolveValidateSessionUploadDir(sessionId).resolve(Paths.get(fileName).getFileName().toString()).normalize();
|
||||
}
|
||||
|
||||
public List<String> listValidateSessionFileNames(String sessionId) {
|
||||
Path uploadDir = resolveValidateSessionUploadDir(sessionId);
|
||||
if (!Files.exists(uploadDir) || !Files.isDirectory(uploadDir)) {
|
||||
return new ArrayList<String>();
|
||||
}
|
||||
try {
|
||||
List<String> result = new ArrayList<String>();
|
||||
Files.list(uploadDir)
|
||||
.filter(Files::isRegularFile)
|
||||
.forEach(path -> result.add(path.getFileName().toString()));
|
||||
return result;
|
||||
} catch (IOException exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "鏍¢獙鏂囦欢闃舵锛氳鍙栦細璇濇枃浠跺垪琛ㄥけ璐?");
|
||||
}
|
||||
}
|
||||
|
||||
public String readTempBatchMeta(String reportId, String batchId) {
|
||||
Path metaPath = resolveTempBatchMetaPath(reportId, batchId);
|
||||
if (!Files.exists(metaPath) || !Files.isRegularFile(metaPath)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return new String(Files.readAllBytes(metaPath), StandardCharsets.UTF_8);
|
||||
} catch (IOException exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "璇诲彇瀵煎叆鎵规淇℃伅澶辫触");
|
||||
}
|
||||
}
|
||||
|
||||
public String saveLedgerSummaryFromTemp(String reportId, String batchId, String fileName) {
|
||||
return savePathFile(reportId, TestReportConst.STORAGE_LEDGER_SUMMARY_DIR,
|
||||
resolveTempBatchFile(reportId, batchId, fileName), fileName, "淇濆瓨鍙拌处姹囨€籈xcel澶辫触");
|
||||
}
|
||||
|
||||
public String savePointExcelFromTemp(String reportId, String batchId, String fileName) {
|
||||
return savePathFile(reportId, TestReportConst.STORAGE_POINT_EXCEL_DIR,
|
||||
resolveTempBatchFile(reportId, batchId, fileName), fileName, "淇濆瓨鐩戞祴鐐笶xcel闄勪欢澶辫触");
|
||||
}
|
||||
|
||||
public String savePointWordFromTemp(String reportId, String batchId, String fileName) {
|
||||
return savePathFile(reportId, TestReportConst.STORAGE_POINT_WORD_DIR,
|
||||
resolveTempBatchFile(reportId, batchId, fileName), fileName, "淇濆瓨鐩戞祴鐐筗ord闄勪欢澶辫触");
|
||||
}
|
||||
|
||||
public String saveGeneratedPlaceholder(String reportId, String fileName, byte[] content) {
|
||||
@@ -58,7 +175,7 @@ public class TestReportStorageService {
|
||||
Files.createDirectories(targetPath.getParent());
|
||||
Files.write(targetPath, content);
|
||||
} catch (IOException exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "保存生成报告失败");
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "淇濆瓨鐢熸垚鎶ュ憡澶辫触");
|
||||
}
|
||||
return buildRelativePath(reportId, TestReportConst.STORAGE_GENERATED_REPORT_DIR, fileName);
|
||||
}
|
||||
@@ -74,7 +191,85 @@ public class TestReportStorageService {
|
||||
try {
|
||||
Files.deleteIfExists(resolveBusinessPath(relativePath));
|
||||
} catch (IOException ignored) {
|
||||
// 文件清理失败不影响主流程
|
||||
// 鏂囦欢娓呯悊澶辫触涓嶅奖鍝嶄富娴佺▼
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteTempBatchQuietly(String reportId, String batchId) {
|
||||
if (StrUtil.isBlank(reportId) || StrUtil.isBlank(batchId)) {
|
||||
return;
|
||||
}
|
||||
deletePathQuietly(resolveTempBatchDir(reportId, batchId));
|
||||
}
|
||||
|
||||
private String saveMultipartFile(String reportId, String childDir, MultipartFile file, String errorMessage) {
|
||||
String originalFileName = file == null ? null : file.getOriginalFilename();
|
||||
if (file == null || file.isEmpty() || StrUtil.isBlank(originalFileName)) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "文件不能为空");
|
||||
}
|
||||
return savePathFile(reportId, childDir, toTempTransferredPath(file), originalFileName, errorMessage);
|
||||
}
|
||||
|
||||
private Path toTempTransferredPath(MultipartFile file) {
|
||||
try {
|
||||
Path tempFile = Files.createTempFile("test-report-", ".upload");
|
||||
file.transferTo(tempFile.toFile());
|
||||
return tempFile;
|
||||
} catch (IOException exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "文件上传失败");
|
||||
}
|
||||
}
|
||||
|
||||
private String savePathFile(String reportId, String childDir, Path sourcePath, String originalFileName, String errorMessage) {
|
||||
String normalizedFileName = originalFileName == null ? null : Paths.get(originalFileName).getFileName().toString();
|
||||
if (sourcePath == null || StrUtil.isBlank(normalizedFileName) || !Files.exists(sourcePath)) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "文件不能为空");
|
||||
}
|
||||
String fileName = UUID.randomUUID().toString().replace("-", "") + "-" + normalizedFileName;
|
||||
Path targetPath = resolveReportDir(reportId, childDir).resolve(fileName);
|
||||
try {
|
||||
Files.createDirectories(targetPath.getParent());
|
||||
Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch (IOException exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, errorMessage);
|
||||
} finally {
|
||||
deletePathQuietlyIfTemp(sourcePath);
|
||||
}
|
||||
return buildRelativePath(reportId, childDir, fileName);
|
||||
}
|
||||
|
||||
private void deletePathQuietlyIfTemp(Path path) {
|
||||
if (path == null) {
|
||||
return;
|
||||
}
|
||||
String fileName = path.getFileName() == null ? null : path.getFileName().toString();
|
||||
if (fileName != null && fileName.startsWith("test-report-")) {
|
||||
deletePathQuietly(path);
|
||||
}
|
||||
}
|
||||
|
||||
private void deletePathQuietly(Path path) {
|
||||
if (path == null || !Files.exists(path)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (Files.isDirectory(path)) {
|
||||
Files.walk(path)
|
||||
.sorted((left, right) -> right.getNameCount() - left.getNameCount())
|
||||
.forEach(this::deleteSingleQuietly);
|
||||
return;
|
||||
}
|
||||
Files.deleteIfExists(path);
|
||||
} catch (IOException ignored) {
|
||||
// 涓存椂鐩綍娓呯悊澶辫触涓嶅奖鍝嶄富娴佺▼
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteSingleQuietly(Path path) {
|
||||
try {
|
||||
Files.deleteIfExists(path);
|
||||
} catch (IOException ignored) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +277,81 @@ public class TestReportStorageService {
|
||||
return Paths.get(filesRootPath, TestReportConst.STORAGE_DIR, reportId, childDir).normalize();
|
||||
}
|
||||
|
||||
private Path resolveTempBatchDir(String reportId, String batchId) {
|
||||
return Paths.get(filesRootPath, TestReportConst.STORAGE_DIR, reportId,
|
||||
TestReportConst.STORAGE_IMPORT_TEMP_DIR, batchId).normalize();
|
||||
}
|
||||
|
||||
private Path resolveTempUploadDir(String reportId, String batchId) {
|
||||
return resolveTempBatchDir(reportId, batchId).resolve(TestReportConst.STORAGE_IMPORT_TEMP_UPLOAD_DIR);
|
||||
}
|
||||
|
||||
private Path resolveTempBatchMetaPath(String reportId, String batchId) {
|
||||
return resolveTempBatchDir(reportId, batchId).resolve(TestReportConst.STORAGE_IMPORT_META_FILE_NAME);
|
||||
}
|
||||
|
||||
private Path resolveValidateSessionDir(String sessionId) {
|
||||
return Paths.get(filesRootPath, TestReportConst.STORAGE_DIR,
|
||||
TestReportConst.STORAGE_VALIDATE_SESSION_DIR, sessionId).normalize();
|
||||
}
|
||||
|
||||
private Path resolveValidateSessionUploadDir(String sessionId) {
|
||||
return resolveValidateSessionDir(sessionId).resolve(TestReportConst.STORAGE_IMPORT_TEMP_UPLOAD_DIR);
|
||||
}
|
||||
|
||||
private Path resolveValidateSessionResultPath(String sessionId) {
|
||||
return resolveValidateSessionDir(sessionId).resolve(TestReportConst.STORAGE_VALIDATE_RESULT_FILE_NAME);
|
||||
}
|
||||
|
||||
private String buildRelativePath(String reportId, String childDir, String fileName) {
|
||||
return reportId + "/" + childDir + "/" + fileName;
|
||||
}
|
||||
|
||||
private String buildTempBatchRelativePath(String reportId, String batchId) {
|
||||
return reportId + "/" + TestReportConst.STORAGE_IMPORT_TEMP_DIR + "/" + batchId;
|
||||
}
|
||||
|
||||
private String buildTempUploadRelativePath(String reportId, String batchId, String fileName) {
|
||||
return buildTempBatchRelativePath(reportId, batchId) + "/"
|
||||
+ TestReportConst.STORAGE_IMPORT_TEMP_UPLOAD_DIR + "/" + fileName;
|
||||
}
|
||||
|
||||
private String buildValidateSessionRelativePath(String sessionId) {
|
||||
return TestReportConst.STORAGE_VALIDATE_SESSION_DIR + "/" + sessionId;
|
||||
}
|
||||
|
||||
private String buildValidateSessionUploadRelativePath(String sessionId, String fileName) {
|
||||
return buildValidateSessionRelativePath(sessionId) + "/"
|
||||
+ TestReportConst.STORAGE_IMPORT_TEMP_UPLOAD_DIR + "/" + fileName;
|
||||
}
|
||||
|
||||
public static class UploadedBatch {
|
||||
private String batchId;
|
||||
private String tempStoragePath;
|
||||
private Map<String, String> storedFileMap = new LinkedHashMap<String, String>();
|
||||
|
||||
public String getBatchId() {
|
||||
return batchId;
|
||||
}
|
||||
|
||||
public void setBatchId(String batchId) {
|
||||
this.batchId = batchId;
|
||||
}
|
||||
|
||||
public String getTempStoragePath() {
|
||||
return tempStoragePath;
|
||||
}
|
||||
|
||||
public void setTempStoragePath(String tempStoragePath) {
|
||||
this.tempStoragePath = tempStoragePath;
|
||||
}
|
||||
|
||||
public Map<String, String> getStoredFileMap() {
|
||||
return storedFileMap;
|
||||
}
|
||||
|
||||
public void setStoredFileMap(Map<String, String> storedFileMap) {
|
||||
this.storedFileMap = storedFileMap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.njcn.gather.aireport.testreport.pojo.param.TestReportGroupSaveParam;
|
||||
import com.njcn.gather.aireport.testreport.pojo.param.TestReportParam;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportGroupReportVO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportLedgerImportResultVO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportLedgerValidateResultVO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportPointVO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportVO;
|
||||
import com.njcn.gather.aireport.testreport.service.TestReportService;
|
||||
@@ -123,12 +124,24 @@ public class TestReportController extends BaseController {
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE)
|
||||
@ApiOperation("导入台账汇总与监测点附件")
|
||||
@ApiOperation("校验台账汇总与监测点附件")
|
||||
@PostMapping("/{id}/ledger/validate")
|
||||
public HttpResult<TestReportLedgerValidateResultVO> validateLedger(@PathVariable("id") String id,
|
||||
@RequestParam("files") MultipartFile[] files) {
|
||||
String methodDescribe = getMethodDescribe("validateLedger");
|
||||
TestReportLedgerValidateResultVO result = testReportService.validateLedger(id, files);
|
||||
return HttpResultUtil.assembleCommonResponseResult(Boolean.TRUE.equals(result.getSuccess())
|
||||
? CommonResponseEnum.SUCCESS : CommonResponseEnum.FAIL,
|
||||
result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE)
|
||||
@ApiOperation("导入已校验通过的台账批次")
|
||||
@PostMapping("/{id}/ledger/import")
|
||||
public HttpResult<TestReportLedgerImportResultVO> importLedger(@PathVariable("id") String id,
|
||||
@RequestParam("files") MultipartFile[] files) {
|
||||
@RequestBody @Validated TestReportParam.LedgerImportParam param) {
|
||||
String methodDescribe = getMethodDescribe("importLedger");
|
||||
TestReportLedgerImportResultVO result = testReportService.importLedger(id, files);
|
||||
TestReportLedgerImportResultVO result = testReportService.importLedger(id, param);
|
||||
return HttpResultUtil.assembleCommonResponseResult(Boolean.TRUE.equals(result.getSuccess())
|
||||
? CommonResponseEnum.SUCCESS : CommonResponseEnum.FAIL,
|
||||
result, methodDescribe);
|
||||
|
||||
@@ -29,6 +29,11 @@ public final class TestReportConst {
|
||||
public static final String STORAGE_POINT_EXCEL_DIR = "point-excel";
|
||||
public static final String STORAGE_POINT_WORD_DIR = "point-word";
|
||||
public static final String STORAGE_GENERATED_REPORT_DIR = "generated-report";
|
||||
public static final String STORAGE_IMPORT_TEMP_DIR = "import-temp";
|
||||
public static final String STORAGE_IMPORT_TEMP_UPLOAD_DIR = "upload";
|
||||
public static final String STORAGE_IMPORT_META_FILE_NAME = "batch-meta.json";
|
||||
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_GUIDE_SHEET_NAME = "填写说明";
|
||||
|
||||
@@ -83,4 +83,11 @@ public class TestReportParam {
|
||||
@Size(max = 128, message = "\u5ba1\u6838\u610f\u89c1\u4e0d\u80fd\u8d85\u8fc7128\u4e2a\u5b57\u7b26")
|
||||
private String checkResult;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class LedgerImportParam {
|
||||
@ApiModelProperty("\u6821\u9a8c\u6210\u529f\u7684\u5bfc\u5165\u6279\u6b21ID")
|
||||
@NotBlank(message = "\u5bfc\u5165\u6279\u6b21ID\u4e0d\u80fd\u4e3a\u7a7a")
|
||||
private String batchId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,13 @@ import java.util.List;
|
||||
@Data
|
||||
public class TestReportLedgerImportResultVO {
|
||||
|
||||
private String batchId;
|
||||
private String currentStage;
|
||||
private Boolean success;
|
||||
private String summaryFileName;
|
||||
private String tempStoragePath;
|
||||
private Integer totalFileCount;
|
||||
private Integer uploadedFileCount;
|
||||
private Integer pointCount;
|
||||
private Integer excelAttachmentCount;
|
||||
private Integer wordAttachmentCount;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.njcn.gather.aireport.testreport.pojo.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TestReportLedgerValidateAttachmentVO {
|
||||
|
||||
private String attachmentName;
|
||||
private String attachmentType;
|
||||
private Boolean referenced;
|
||||
private Boolean uploaded;
|
||||
private Boolean missing;
|
||||
private Boolean reuploadAllowed;
|
||||
private String message;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.njcn.gather.aireport.testreport.pojo.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class TestReportLedgerValidatePointVO {
|
||||
|
||||
private Integer rowNo;
|
||||
private String pointKey;
|
||||
private String substationName;
|
||||
private String pointName;
|
||||
private Boolean complete;
|
||||
private Integer missingAttachmentCount;
|
||||
private Boolean testDeviceNoValid;
|
||||
private String testDeviceNoMessage;
|
||||
private Boolean clientUnitNameValid;
|
||||
private String clientUnitNameMessage;
|
||||
private List<TestReportLedgerValidateAttachmentVO> attachments = new ArrayList<TestReportLedgerValidateAttachmentVO>();
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.njcn.gather.aireport.testreport.pojo.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class TestReportLedgerValidateResultVO {
|
||||
|
||||
private String sessionId;
|
||||
private Boolean success;
|
||||
private String summaryFileName;
|
||||
private Boolean summaryFilePresent;
|
||||
private String tempStoragePath;
|
||||
private Integer uploadedFileCount;
|
||||
private Integer pointCount;
|
||||
private Integer missingAttachmentCount;
|
||||
private Boolean canContinueUpload;
|
||||
private List<String> requiredSheets = new ArrayList<String>();
|
||||
private List<String> existingSheets = new ArrayList<String>();
|
||||
private List<String> missingSheets = new ArrayList<String>();
|
||||
private List<String> orphanAttachmentNames = new ArrayList<String>();
|
||||
private List<String> failReasons = new ArrayList<String>();
|
||||
private List<TestReportLedgerValidatePointVO> points = new ArrayList<TestReportLedgerValidatePointVO>();
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import com.njcn.gather.aireport.testreport.pojo.param.TestReportParam;
|
||||
import com.njcn.gather.aireport.testreport.pojo.po.TestReportPO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportGroupReportVO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportLedgerImportResultVO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportLedgerValidateResultVO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportPointVO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportVO;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -34,7 +35,9 @@ public interface TestReportService extends IService<TestReportPO> {
|
||||
|
||||
ResponseEntity<byte[]> previewTemplate(String id);
|
||||
|
||||
TestReportLedgerImportResultVO importLedger(String id, MultipartFile[] files);
|
||||
TestReportLedgerValidateResultVO validateLedger(String id, MultipartFile[] files);
|
||||
|
||||
TestReportLedgerImportResultVO importLedger(String id, TestReportParam.LedgerImportParam param);
|
||||
|
||||
ResponseEntity<byte[]> downloadLedgerTemplate();
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.njcn.gather.aireport.testreport.pojo.po.TestReportPointPO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportExcelVO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportGroupReportVO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportLedgerImportResultVO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportLedgerValidateResultVO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportPointVO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportVO;
|
||||
import com.njcn.gather.aireport.testreport.service.TestReportService;
|
||||
@@ -43,6 +44,7 @@ import com.njcn.web.factory.PageFactory;
|
||||
import com.njcn.web.utils.ExcelUtil;
|
||||
import com.njcn.web.utils.RequestUtil;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -70,6 +72,7 @@ import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class TestReportServiceImpl extends ServiceImpl<TestReportMapper, TestReportPO> implements TestReportService {
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
@@ -108,6 +111,7 @@ public class TestReportServiceImpl extends ServiceImpl<TestReportMapper, TestRep
|
||||
private static final String MSG_ONLY_PENDING_CAN_IMPORT = "\u4ec5\u672a\u751f\u6210\u4efb\u52a1\u5141\u8bb8\u5bfc\u5165";
|
||||
private static final String MSG_IMPORT_FILES_REQUIRED = "\u539f\u59cbExcel\u6587\u4ef6\u4e0d\u80fd\u4e3a\u7a7a";
|
||||
private static final String MSG_IMPORT_FILE_NAME_DUPLICATE = "\u539f\u59cbExcel\u6587\u4ef6\u540d\u4e0d\u80fd\u91cd\u590d";
|
||||
private static final String MSG_BATCH_ID_REQUIRED = "\u5bfc\u5165\u6279\u6b21ID\u4e0d\u80fd\u4e3a\u7a7a";
|
||||
private static final String MSG_GROUPS_REQUIRED = "\u5206\u7ec4\u5217\u8868\u4e0d\u80fd\u4e3a\u7a7a";
|
||||
private static final String MSG_GROUP_POINT_DUPLICATE = "\u540c\u4e00\u76d1\u6d4b\u70b9\u4e0d\u80fd\u91cd\u590d\u5206\u7ec4";
|
||||
private static final String MSG_GROUP_POINT_MISSING = "\u6240\u6709\u76d1\u6d4b\u70b9\u90fd\u5fc5\u987b\u5b8c\u6210\u5206\u7ec4";
|
||||
@@ -245,6 +249,14 @@ public class TestReportServiceImpl extends ServiceImpl<TestReportMapper, TestRep
|
||||
.eq(TestReportPO::getStatus, TestReportConst.STATUS_NORMAL)
|
||||
.one();
|
||||
if (report == null) {
|
||||
TestReportPO existingReport = this.getById(normalizedId);
|
||||
if (existingReport == null) {
|
||||
log.warn("testreport requireNormal failed: report not found, reportId={}", normalizedId);
|
||||
} else {
|
||||
log.warn("testreport requireNormal failed: report status invalid, reportId={}, status={}, state={}, reportGenerateState={}, updateTime={}",
|
||||
normalizedId, existingReport.getStatus(), existingReport.getState(),
|
||||
existingReport.getReportGenerateState(), existingReport.getUpdateTime());
|
||||
}
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, MSG_REPORT_NOT_FOUND);
|
||||
}
|
||||
return report;
|
||||
@@ -301,10 +313,24 @@ public class TestReportServiceImpl extends ServiceImpl<TestReportMapper, TestRep
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public TestReportLedgerImportResultVO importLedger(String id, MultipartFile[] files) {
|
||||
public TestReportLedgerValidateResultVO validateLedger(String id, MultipartFile[] files) {
|
||||
String sessionId = requireText(id, MSG_ID_REQUIRED);
|
||||
log.info("testreport ledger validate start: sessionId={}, userId={}, fileCount={}, fileNames={}",
|
||||
sessionId, resolveCurrentUserId(), files == null ? 0 : files.length, describeUploadedFiles(files));
|
||||
return testReportLedgerImportService.validateLedgerSession(sessionId, files);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public TestReportLedgerImportResultVO importLedger(String id, TestReportParam.LedgerImportParam param) {
|
||||
TestReportPO report = requireNormal(id);
|
||||
ensureImportable(report);
|
||||
return testReportLedgerImportService.importLedger(report, files, resolveCurrentUserId());
|
||||
if (param == null || StrUtil.isBlank(param.getBatchId())) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, MSG_BATCH_ID_REQUIRED);
|
||||
}
|
||||
log.info("testreport ledger import start: reportId={}, userId={}, batchId={}",
|
||||
id, resolveCurrentUserId(), param.getBatchId());
|
||||
return testReportLedgerImportService.importLedger(report, param.getBatchId(), resolveCurrentUserId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1044,6 +1070,22 @@ public class TestReportServiceImpl extends ServiceImpl<TestReportMapper, TestRep
|
||||
return StrUtil.isBlank(userId) ? null : userId;
|
||||
}
|
||||
|
||||
private List<String> describeUploadedFiles(MultipartFile[] files) {
|
||||
if (files == null || files.length == 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> fileNames = new ArrayList<String>();
|
||||
for (MultipartFile file : files) {
|
||||
if (file == null) {
|
||||
fileNames.add("null");
|
||||
continue;
|
||||
}
|
||||
String fileName = trimToNull(file.getOriginalFilename());
|
||||
fileNames.add(fileName == null ? "(blank)" : fileName);
|
||||
}
|
||||
return fileNames;
|
||||
}
|
||||
|
||||
private String trimToNull(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
|
||||
@@ -60,7 +60,7 @@ class TestReportLedgerExcelParserTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectDuplicatePointBySubstationDeviceAndPointName() throws Exception {
|
||||
void shouldRejectDuplicatePointBySubstationAndPointName() throws Exception {
|
||||
TestReportLedgerExcelParser parser = new TestReportLedgerExcelParser();
|
||||
MockMultipartFile summaryFile = new MockMultipartFile("files", TestReportConst.LEDGER_SUMMARY_FILE_NAME,
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", buildDuplicatePointWorkbookBytes());
|
||||
|
||||
@@ -2,16 +2,21 @@ package com.njcn.gather.aireport.testreport.component;
|
||||
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.aireport.clientunit.mapper.ClientUnitMapper;
|
||||
import com.njcn.gather.aireport.clientunit.pojo.po.ClientUnitPO;
|
||||
import com.njcn.gather.aireport.testdevice.mapper.TestDeviceMapper;
|
||||
import com.njcn.gather.aireport.testdevice.pojo.constant.TestDeviceConst;
|
||||
import com.njcn.gather.aireport.testdevice.pojo.po.TestDevicePO;
|
||||
import com.njcn.gather.aireport.testreport.mapper.TestReportGroupReportMapper;
|
||||
import com.njcn.gather.aireport.testreport.mapper.TestReportMapper;
|
||||
import com.njcn.gather.aireport.testreport.mapper.TestReportPointMapper;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportLedgerValidateResultVO;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
@@ -47,7 +52,51 @@ class TestReportLedgerImportServiceTest {
|
||||
BusinessException exception = Assertions.assertThrows(BusinessException.class,
|
||||
() -> invokeResolveDeviceState(service, "停用", 4));
|
||||
|
||||
Assertions.assertEquals("基础资料维护阶段:检测设备sheet第4行设备状态不正确", exception.getMessage());
|
||||
Assertions.assertEquals("基础信息维护阶段:检测设备sheet第4行设备状态不正确", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void normalizeFileMapShouldSkipEmptyFileItemsAndKeepTrimmedNames() {
|
||||
TestReportLedgerImportService service = createService();
|
||||
MockMultipartFile validFile = new MockMultipartFile("files", " point-a.xlsx ", "application/octet-stream",
|
||||
new byte[]{1});
|
||||
MockMultipartFile emptyFile = new MockMultipartFile("files", "empty.xlsx", "application/octet-stream",
|
||||
new byte[0]);
|
||||
|
||||
Map<String, MultipartFile> result = invokeNormalizeFileMap(service, new MultipartFile[]{emptyFile, validFile});
|
||||
|
||||
Assertions.assertEquals(1, result.size());
|
||||
Assertions.assertSame(validFile, result.get("point-a.xlsx"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void fillPointValidationResultShouldAppendReferenceErrorsToPointResult() {
|
||||
TestReportLedgerImportService service = createService();
|
||||
TestReportLedgerExcelParser.ParsedPointRow pointRow = new TestReportLedgerExcelParser.ParsedPointRow();
|
||||
pointRow.setRowNo(2);
|
||||
pointRow.setSubstationName("变电站A");
|
||||
pointRow.setPointName("监测点A");
|
||||
pointRow.setTestDeviceNo("DEV-001");
|
||||
pointRow.setClientUnitName("委托单位A");
|
||||
pointRow.setExcelAttachmentName("point-a.xlsx");
|
||||
pointRow.setWordAttachmentName("point-a.docx");
|
||||
|
||||
TestReportLedgerExcelParser.ParsedLedger ledger = new TestReportLedgerExcelParser.ParsedLedger();
|
||||
ledger.setPoints(Collections.singletonList(pointRow));
|
||||
|
||||
TestReportLedgerValidateResultVO result = new TestReportLedgerValidateResultVO();
|
||||
|
||||
invokeFillPointValidationResult(service, result, ledger, Collections.singleton("台账信息汇总.xlsx"),
|
||||
new HashMap<String, TestDevicePO>(), new HashMap<String, ClientUnitPO>());
|
||||
|
||||
Assertions.assertEquals(1, result.getPoints().size());
|
||||
Assertions.assertFalse(Boolean.TRUE.equals(result.getPoints().get(0).getComplete()));
|
||||
Assertions.assertFalse(Boolean.TRUE.equals(result.getPoints().get(0).getTestDeviceNoValid()));
|
||||
Assertions.assertEquals("第2行检测设备编号在数据库和检测设备sheet中都不存在",
|
||||
result.getPoints().get(0).getTestDeviceNoMessage());
|
||||
Assertions.assertFalse(Boolean.TRUE.equals(result.getPoints().get(0).getClientUnitNameValid()));
|
||||
Assertions.assertEquals("第2行委托单位名称在数据库和委托单位sheet中都不存在",
|
||||
result.getPoints().get(0).getClientUnitNameMessage());
|
||||
}
|
||||
|
||||
private TestReportLedgerImportService createService() {
|
||||
@@ -71,6 +120,18 @@ class TestReportLedgerImportServiceTest {
|
||||
new Class[]{String.class, Integer.class}, new Object[]{stateName, rowNo});
|
||||
}
|
||||
|
||||
private void invokeFillPointValidationResult(TestReportLedgerImportService service,
|
||||
TestReportLedgerValidateResultVO result,
|
||||
TestReportLedgerExcelParser.ParsedLedger ledger,
|
||||
java.util.Set<String> sessionFileNameSet,
|
||||
Map<String, TestDevicePO> dbDeviceMap,
|
||||
Map<String, ClientUnitPO> dbClientMap) {
|
||||
invokePrivateMethod(service, "fillPointValidationResult",
|
||||
new Class[]{TestReportLedgerValidateResultVO.class, TestReportLedgerExcelParser.ParsedLedger.class,
|
||||
java.util.Set.class, Map.class, Map.class},
|
||||
new Object[]{result, ledger, sessionFileNameSet, dbDeviceMap, dbClientMap});
|
||||
}
|
||||
|
||||
private Object invokePrivateMethod(TestReportLedgerImportService service, String methodName,
|
||||
Class<?>[] parameterTypes, Object[] args) {
|
||||
try {
|
||||
@@ -87,18 +148,4 @@ class TestReportLedgerImportServiceTest {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void normalizeFileMapShouldSkipEmptyFileItemsAndKeepTrimmedNames() {
|
||||
TestReportLedgerImportService service = createService();
|
||||
MockMultipartFile validFile = new MockMultipartFile("files", " point-a.xlsx ", "application/octet-stream",
|
||||
new byte[]{1});
|
||||
MockMultipartFile emptyFile = new MockMultipartFile("files", "empty.xlsx", "application/octet-stream",
|
||||
new byte[0]);
|
||||
|
||||
Map<String, MultipartFile> result = invokeNormalizeFileMap(service, new MultipartFile[]{emptyFile, validFile});
|
||||
|
||||
Assertions.assertEquals(1, result.size());
|
||||
Assertions.assertSame(validFile, result.get("point-a.xlsx"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.njcn.gather.aireport.testreport.pojo.param.TestReportParam;
|
||||
import com.njcn.gather.aireport.testreport.pojo.po.TestReportGroupReportPO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.po.TestReportPO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.po.TestReportPointPO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportLedgerValidateResultVO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportPointVO;
|
||||
import com.njcn.gather.aireport.testreport.pojo.vo.TestReportVO;
|
||||
import com.njcn.gather.comservice.filepreview.FilePreviewService;
|
||||
@@ -39,6 +40,7 @@ import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class TestReportServiceImplTest {
|
||||
@@ -81,7 +83,9 @@ class TestReportServiceImplTest {
|
||||
@Test
|
||||
void serviceShouldExposeGroupedApis() throws Exception {
|
||||
Assertions.assertNotNull(TestReportServiceImpl.class.getMethod("listTestReports", TestReportParam.QueryParam.class));
|
||||
Assertions.assertNotNull(TestReportServiceImpl.class.getMethod("importLedger", String.class, MultipartFile[].class));
|
||||
Assertions.assertNotNull(TestReportServiceImpl.class.getMethod("validateLedger", String.class, MultipartFile[].class));
|
||||
Assertions.assertNotNull(TestReportServiceImpl.class.getMethod("importLedger", String.class,
|
||||
TestReportParam.LedgerImportParam.class));
|
||||
Assertions.assertNotNull(TestReportServiceImpl.class.getMethod("listPoints", String.class));
|
||||
Assertions.assertNotNull(TestReportServiceImpl.class.getMethod("listGroupReports", String.class));
|
||||
}
|
||||
@@ -114,11 +118,22 @@ class TestReportServiceImplTest {
|
||||
MockMultipartFile file = new MockMultipartFile("files", "sample.xls", "application/vnd.ms-excel", new byte[]{1});
|
||||
|
||||
BusinessException exception = Assertions.assertThrows(BusinessException.class,
|
||||
() -> service.importLedger("report-001", new MultipartFile[]{file}));
|
||||
() -> service.validateLedger("report-001", new MultipartFile[]{file}));
|
||||
|
||||
Assertions.assertTrue(exception.getMessage().contains("未生成"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void importLedgerShouldRejectMissingBatchId() {
|
||||
TestReportServiceImpl service = createService(mock(IDictTypeService.class), mock(IDictDataService.class));
|
||||
TestReportParam.LedgerImportParam param = new TestReportParam.LedgerImportParam();
|
||||
|
||||
BusinessException exception = Assertions.assertThrows(BusinessException.class,
|
||||
() -> service.importLedger("report-001", param));
|
||||
|
||||
Assertions.assertNotNull(exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void addTestReportShouldRejectMissingNo() {
|
||||
TestReportServiceImpl service = createService(mock(IDictTypeService.class), mock(IDictDataService.class));
|
||||
|
||||
Reference in New Issue
Block a user