Compare commits
3 Commits
2026-06
...
d8ae012bd2
| Author | SHA1 | Date | |
|---|---|---|---|
| d8ae012bd2 | |||
|
|
fa1a285e04 | ||
| f5f828cd05 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -52,3 +52,8 @@ rebel.xml
|
||||
/.fastRequest/collections/Root/Default Group/directory.json
|
||||
/.fastRequest/collections/Root/directory.json
|
||||
/.fastRequest/config/fastRequestCurrentProjectConfig.json
|
||||
/ai-report/report-approval/src/main/resources/sql/report-approval/
|
||||
/ai-report/test-report/src/main/resources/sql/test-report/
|
||||
/ai-report/client-unit/src/main/resources/sql/client-unit/
|
||||
/ai-report/report-model/src/main/resources/sql/report-model/
|
||||
/ai-report/test-device/src/main/resources/sql/test-device/
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.njcn.gather.aireport.clientunit.mapper.ClientUnitMapper">
|
||||
|
||||
</mapper>
|
||||
@@ -26,6 +26,18 @@
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.njcn.gather</groupId>
|
||||
<artifactId>client-unit</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.njcn.gather</groupId>
|
||||
<artifactId>test-device</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.njcn</groupId>
|
||||
<artifactId>njcn-common</artifactId>
|
||||
@@ -50,6 +62,12 @@
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>4.1.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
|
||||
@@ -0,0 +1,362 @@
|
||||
package com.njcn.gather.aireport.testreport.component;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.aireport.testreport.pojo.constant.TestReportConst;
|
||||
import lombok.Data;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.DataFormatter;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.usermodel.WorkbookFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Component
|
||||
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(
|
||||
"设备型号", "设备编号", "标准有效期", "设备状态", "标准报告文件名");
|
||||
private static final List<String> CLIENT_HEADERS = Arrays.asList(
|
||||
"单位名称", "联系人", "联系方式", "地址");
|
||||
|
||||
public ParsedLedger parse(MultipartFile summaryFile, Map<String, MultipartFile> attachments) {
|
||||
String fileName = summaryFile == null ? null : StrUtil.trimToNull(summaryFile.getOriginalFilename());
|
||||
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 (Workbook workbook = WorkbookFactory.create(inputStream)) {
|
||||
ParsedLedger ledger = new ParsedLedger();
|
||||
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;
|
||||
} catch (IOException exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:读取台账汇总excel失败");
|
||||
} catch (Exception exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:解析台账汇总excel失败");
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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>();
|
||||
Set<String> excelNames = new HashSet<String>();
|
||||
Set<String> wordNames = new HashSet<String>();
|
||||
for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
|
||||
Row row = sheet.getRow(rowIndex);
|
||||
if (isEmptyRow(row, POINT_HEADERS.size())) {
|
||||
continue;
|
||||
}
|
||||
ParsedPointRow point = new ParsedPointRow();
|
||||
point.setRowNo(rowIndex + 1);
|
||||
point.setGroupNo(parsePositiveInteger(optionalCell(row, 0), "第" + (rowIndex + 1) + "行分组号必须为正整数"));
|
||||
point.setSubstationName(requireCell(row, 1, rowIndex, "变电站"));
|
||||
point.setPointName(requireCell(row, 2, rowIndex, "监测点名称"));
|
||||
point.setTestDeviceNo(optionalCell(row, 3));
|
||||
point.setClientUnitName(optionalCell(row, 4));
|
||||
point.setExcelAttachmentName(requireCell(row, 5, rowIndex, "Excel附件名称"));
|
||||
point.setWordAttachmentName(requireCell(row, 6, rowIndex, "Word附件名称"));
|
||||
validateExtension(point.getExcelAttachmentName(), Arrays.asList(".xls", ".xlsx"),
|
||||
"第" + (rowIndex + 1) + "行Excel附件扩展名仅允许xls/xlsx");
|
||||
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, "校验文件阶段:监测点信息中存在重复点位");
|
||||
}
|
||||
if (!excelNames.add(point.getExcelAttachmentName())) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:监测点信息中存在重复Excel附件名称");
|
||||
}
|
||||
if (!wordNames.add(point.getWordAttachmentName())) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:监测点信息中存在重复Word附件名称");
|
||||
}
|
||||
points.add(point);
|
||||
}
|
||||
if (points.isEmpty()) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:监测点信息sheet不能为空");
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
||||
private List<ParsedDeviceRow> parseDeviceSheet(Sheet sheet) {
|
||||
validateHeaders(sheet, DEVICE_HEADERS, "检测设备");
|
||||
List<ParsedDeviceRow> devices = new ArrayList<ParsedDeviceRow>();
|
||||
Set<String> deviceNos = new HashSet<String>();
|
||||
for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
|
||||
Row row = sheet.getRow(rowIndex);
|
||||
if (isEmptyRow(row, DEVICE_HEADERS.size())) {
|
||||
continue;
|
||||
}
|
||||
ParsedDeviceRow device = new ParsedDeviceRow();
|
||||
device.setRowNo(rowIndex + 1);
|
||||
device.setTypeName(requireCell(row, 0, rowIndex, "设备型号"));
|
||||
device.setNo(requireCell(row, 1, rowIndex, "设备编号"));
|
||||
device.setValidityPeriod(parseDate(requireCell(row, 2, rowIndex, "标准有效期"), rowIndex));
|
||||
device.setStateName(requireCell(row, 3, rowIndex, "设备状态"));
|
||||
device.setValidityReportName(requireCell(row, 4, rowIndex, "标准报告文件名"));
|
||||
if (!deviceNos.add(device.getNo())) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:检测设备sheet中存在重复设备编号");
|
||||
}
|
||||
devices.add(device);
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
private List<ParsedClientRow> parseClientSheet(Sheet sheet) {
|
||||
validateHeaders(sheet, CLIENT_HEADERS, "委托单位");
|
||||
List<ParsedClientRow> clients = new ArrayList<ParsedClientRow>();
|
||||
Set<String> clientNames = new HashSet<String>();
|
||||
for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
|
||||
Row row = sheet.getRow(rowIndex);
|
||||
if (isEmptyRow(row, CLIENT_HEADERS.size())) {
|
||||
continue;
|
||||
}
|
||||
ParsedClientRow client = new ParsedClientRow();
|
||||
client.setRowNo(rowIndex + 1);
|
||||
client.setName(requireCell(row, 0, rowIndex, "单位名称"));
|
||||
client.setContactName(optionalCell(row, 1));
|
||||
client.setPhonenumber(optionalCell(row, 2));
|
||||
client.setAddress(optionalCell(row, 3));
|
||||
if (!clientNames.add(client.getName())) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:委托单位sheet中存在重复单位名称");
|
||||
}
|
||||
clients.add(client);
|
||||
}
|
||||
return clients;
|
||||
}
|
||||
|
||||
private void validateReferencedAttachments(List<ParsedPointRow> points, Set<String> attachmentNames) {
|
||||
Set<String> referencedNames = new HashSet<String>();
|
||||
for (ParsedPointRow point : points) {
|
||||
if (StrUtil.isNotBlank(point.getExcelAttachmentName())) {
|
||||
referencedNames.add(point.getExcelAttachmentName());
|
||||
}
|
||||
if (StrUtil.isNotBlank(point.getWordAttachmentName())) {
|
||||
referencedNames.add(point.getWordAttachmentName());
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateHeaders(Sheet sheet, List<String> expectedHeaders, String sheetName) {
|
||||
Row headerRow = sheet.getRow(0);
|
||||
if (headerRow == null) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:" + sheetName + "sheet缺少表头");
|
||||
}
|
||||
for (int i = 0; i < expectedHeaders.size(); i++) {
|
||||
String actualHeader = optionalCell(headerRow, i);
|
||||
if (!expectedHeaders.get(i).equals(actualHeader)) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:" + sheetName + "sheet表头不符合模板要求");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isEmptyRow(Row row, int cellCount) {
|
||||
if (row == null) {
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < cellCount; i++) {
|
||||
if (StrUtil.isNotBlank(optionalCell(row, i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private String requireCell(Row row, int cellIndex, int rowIndex, String fieldName) {
|
||||
String value = optionalCell(row, cellIndex);
|
||||
if (StrUtil.isBlank(value)) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:第" + (rowIndex + 1) + "行缺少" + fieldName);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private String optionalCell(Row row, int cellIndex) {
|
||||
if (row == null) {
|
||||
return null;
|
||||
}
|
||||
Cell cell = row.getCell(cellIndex);
|
||||
return cell == null ? null : StrUtil.trimToNull(FORMATTER.formatCellValue(cell));
|
||||
}
|
||||
|
||||
private Integer parsePositiveInteger(String value, String message) {
|
||||
if (StrUtil.isBlank(value)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
int parsed = Integer.parseInt(value);
|
||||
if (parsed <= 0) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:" + message);
|
||||
}
|
||||
return parsed;
|
||||
} catch (NumberFormatException exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:" + message);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateExtension(String fileName, List<String> validExtensions, String message) {
|
||||
String lowerFileName = fileName.toLowerCase();
|
||||
for (String validExtension : validExtensions) {
|
||||
if (lowerFileName.endsWith(validExtension)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "校验文件阶段:" + message);
|
||||
}
|
||||
|
||||
private LocalDate parseDate(String value, int rowIndex) {
|
||||
try {
|
||||
return LocalDate.parse(value, DATE_FORMATTER);
|
||||
} catch (DateTimeParseException exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL,
|
||||
"校验文件阶段:检测设备sheet第" + (rowIndex + 1) + "行标准有效期格式必须为yyyy-MM-dd");
|
||||
}
|
||||
}
|
||||
|
||||
@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>();
|
||||
|
||||
public Map<String, ParsedDeviceRow> toDeviceMap() {
|
||||
Map<String, ParsedDeviceRow> result = new HashMap<String, ParsedDeviceRow>();
|
||||
for (ParsedDeviceRow device : devices) {
|
||||
result.put(device.getNo(), device);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Map<String, ParsedClientRow> toClientMap() {
|
||||
Map<String, ParsedClientRow> result = new HashMap<String, ParsedClientRow>();
|
||||
for (ParsedClientRow client : clients) {
|
||||
result.put(client.getName(), client);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ParsedPointRow {
|
||||
private Integer rowNo;
|
||||
private Integer groupNo;
|
||||
private String substationName;
|
||||
private String pointName;
|
||||
private String testDeviceNo;
|
||||
private String clientUnitName;
|
||||
private String excelAttachmentName;
|
||||
private String wordAttachmentName;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ParsedDeviceRow {
|
||||
private Integer rowNo;
|
||||
private String typeName;
|
||||
private String no;
|
||||
private LocalDate validityPeriod;
|
||||
private String stateName;
|
||||
private String validityReportName;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ParsedClientRow {
|
||||
private Integer rowNo;
|
||||
private String name;
|
||||
private String contactName;
|
||||
private String phonenumber;
|
||||
private String address;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,875 @@
|
||||
package com.njcn.gather.aireport.testreport.component;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.aireport.clientunit.mapper.ClientUnitMapper;
|
||||
import com.njcn.gather.aireport.clientunit.pojo.constant.ClientUnitConst;
|
||||
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.testdevice.pojo.vo.TestDeviceTypeNameVO;
|
||||
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.constant.TestReportConst;
|
||||
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.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;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
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;
|
||||
private final TestReportGroupReportMapper testReportGroupReportMapper;
|
||||
private final TestDeviceMapper testDeviceMapper;
|
||||
private final ClientUnitMapper clientUnitMapper;
|
||||
private final TestReportLedgerExcelParser testReportLedgerExcelParser;
|
||||
private final TestReportStorageService testReportStorageService;
|
||||
|
||||
public TestReportLedgerImportService(TestReportMapper testReportMapper,
|
||||
TestReportPointMapper testReportPointMapper,
|
||||
TestReportGroupReportMapper testReportGroupReportMapper,
|
||||
TestDeviceMapper testDeviceMapper,
|
||||
ClientUnitMapper clientUnitMapper,
|
||||
TestReportLedgerExcelParser testReportLedgerExcelParser,
|
||||
TestReportStorageService testReportStorageService) {
|
||||
this.testReportMapper = testReportMapper;
|
||||
this.testReportPointMapper = testReportPointMapper;
|
||||
this.testReportGroupReportMapper = testReportGroupReportMapper;
|
||||
this.testDeviceMapper = testDeviceMapper;
|
||||
this.clientUnitMapper = clientUnitMapper;
|
||||
this.testReportLedgerExcelParser = testReportLedgerExcelParser;
|
||||
this.testReportStorageService = testReportStorageService;
|
||||
}
|
||||
|
||||
public TestReportLedgerValidateResultVO validateLedgerSession(String sessionId, MultipartFile[] files) {
|
||||
Map<String, MultipartFile> fileMap = normalizeFileMap(files);
|
||||
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;
|
||||
}
|
||||
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));
|
||||
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.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);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (files == null || files.length == 0) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "选择文件阶段:导入文件不能为空");
|
||||
}
|
||||
Map<String, MultipartFile> fileMap = new LinkedHashMap<String, MultipartFile>();
|
||||
for (MultipartFile file : files) {
|
||||
if (file == null || file.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
String fileName = normalizeFileName(file.getOriginalFilename());
|
||||
if (fileName == null) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "选择文件阶段:文件名不能为空");
|
||||
}
|
||||
if (fileMap.containsKey(fileName)) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "选择文件阶段:存在重复文件名 " + fileName);
|
||||
}
|
||||
fileMap.put(fileName, file);
|
||||
}
|
||||
if (fileMap.isEmpty()) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "选择文件阶段:导入文件不能为空");
|
||||
}
|
||||
return fileMap;
|
||||
}
|
||||
|
||||
private void validatePointReferences(List<TestReportLedgerExcelParser.ParsedPointRow> points,
|
||||
Map<String, TestDevicePO> dbDeviceMap,
|
||||
Map<String, ClientUnitPO> dbClientMap,
|
||||
Map<String, TestReportLedgerExcelParser.ParsedDeviceRow> sheetDeviceMap,
|
||||
Map<String, TestReportLedgerExcelParser.ParsedClientRow> sheetClientMap) {
|
||||
for (TestReportLedgerExcelParser.ParsedPointRow point : points) {
|
||||
if (StrUtil.isNotBlank(point.getTestDeviceNo())
|
||||
&& !dbDeviceMap.containsKey(point.getTestDeviceNo())
|
||||
&& !sheetDeviceMap.containsKey(point.getTestDeviceNo())) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL,
|
||||
"校验文件阶段:第" + point.getRowNo() + "行检测设备编号在数据库和检测设备sheet中都不存在");
|
||||
}
|
||||
if (StrUtil.isNotBlank(point.getClientUnitName())
|
||||
&& !dbClientMap.containsKey(point.getClientUnitName())
|
||||
&& !sheetClientMap.containsKey(point.getClientUnitName())) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL,
|
||||
"校验文件阶段:第" + point.getRowNo() + "行委托单位名称在数据库和委托单位sheet中都不存在");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private BaseDataSummary maintainBaseData(TestReportLedgerExcelParser.ParsedLedger ledger,
|
||||
Map<String, TestDevicePO> dbDeviceMap,
|
||||
Map<String, ClientUnitPO> dbClientMap,
|
||||
String userId) {
|
||||
BaseDataSummary summary = new BaseDataSummary();
|
||||
Map<String, String> typeNameIdMap = loadDeviceTypeNameIdMap();
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
for (TestReportLedgerExcelParser.ParsedDeviceRow deviceRow : ledger.getDevices()) {
|
||||
TestDevicePO existingDevice = dbDeviceMap.get(deviceRow.getNo());
|
||||
if (existingDevice != null) {
|
||||
summary.setDeviceReuseCount(summary.getDeviceReuseCount() + 1);
|
||||
continue;
|
||||
}
|
||||
String typeId = typeNameIdMap.get(deviceRow.getTypeName());
|
||||
if (StrUtil.isBlank(typeId)) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL,
|
||||
"基础信息维护阶段:检测设备sheet第" + deviceRow.getRowNo() + "行设备型号不存在");
|
||||
}
|
||||
TestDevicePO device = new TestDevicePO();
|
||||
device.setId(UUID.randomUUID().toString());
|
||||
device.setType(typeId);
|
||||
device.setNo(deviceRow.getNo());
|
||||
device.setValidityPeriod(deviceRow.getValidityPeriod());
|
||||
device.setValidityReport(deviceRow.getValidityReportName());
|
||||
device.setState(resolveDeviceState(deviceRow.getStateName(), deviceRow.getRowNo()));
|
||||
device.setStatus(TestDeviceConst.STATUS_NORMAL);
|
||||
device.setCreateBy(userId);
|
||||
device.setCreateTime(now);
|
||||
device.setUpdateBy(userId);
|
||||
device.setUpdateTime(now);
|
||||
testDeviceMapper.insert(device);
|
||||
dbDeviceMap.put(device.getNo(), device);
|
||||
summary.setDeviceAddedCount(summary.getDeviceAddedCount() + 1);
|
||||
}
|
||||
|
||||
for (TestReportLedgerExcelParser.ParsedClientRow clientRow : ledger.getClients()) {
|
||||
ClientUnitPO existingClient = dbClientMap.get(clientRow.getName());
|
||||
if (existingClient != null) {
|
||||
summary.setClientReuseCount(summary.getClientReuseCount() + 1);
|
||||
continue;
|
||||
}
|
||||
ClientUnitPO client = new ClientUnitPO();
|
||||
client.setId(UUID.randomUUID().toString());
|
||||
client.setName(clientRow.getName());
|
||||
client.setContactName(defaultEmpty(clientRow.getContactName()));
|
||||
client.setPhonenumber(defaultEmpty(clientRow.getPhonenumber()));
|
||||
client.setAddress(clientRow.getAddress());
|
||||
client.setStatus(ClientUnitConst.STATUS_NORMAL);
|
||||
client.setCreateBy(userId);
|
||||
client.setCreateTime(now);
|
||||
client.setUpdateBy(userId);
|
||||
client.setUpdateTime(now);
|
||||
clientUnitMapper.insert(client);
|
||||
dbClientMap.put(client.getName(), client);
|
||||
summary.setClientAddedCount(summary.getClientAddedCount() + 1);
|
||||
}
|
||||
return summary;
|
||||
}
|
||||
|
||||
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) {
|
||||
TestReportPointPO point = new TestReportPointPO();
|
||||
point.setId(UUID.randomUUID().toString());
|
||||
point.setReportId(reportId);
|
||||
point.setGroupNo(pointRow.getGroupNo());
|
||||
point.setSubstationName(pointRow.getSubstationName());
|
||||
point.setPointName(pointRow.getPointName());
|
||||
point.setTestDeviceNo(pointRow.getTestDeviceNo());
|
||||
point.setClientUnitName(pointRow.getClientUnitName());
|
||||
point.setExcelAttachmentName(pointRow.getExcelAttachmentName());
|
||||
point.setExcelStoragePath(testReportStorageService.savePointExcelFromTemp(reportId, batchId,
|
||||
pointRow.getExcelAttachmentName()));
|
||||
point.setWordAttachmentName(pointRow.getWordAttachmentName());
|
||||
point.setWordStoragePath(testReportStorageService.savePointWordFromTemp(reportId, batchId,
|
||||
pointRow.getWordAttachmentName()));
|
||||
point.setSortNo(sortNo++);
|
||||
point.setDeleted(TestReportConst.DELETED_NO);
|
||||
point.setCreateBy(userId);
|
||||
point.setCreateTime(now);
|
||||
point.setUpdateBy(userId);
|
||||
point.setUpdateTime(now);
|
||||
testReportPointMapper.insert(point);
|
||||
if (pointRow.getGroupNo() != null) {
|
||||
groupNos.add(pointRow.getGroupNo());
|
||||
}
|
||||
}
|
||||
return groupNos.size();
|
||||
}
|
||||
|
||||
private void deleteOldFiles(List<TestReportPointPO> oldPoints, List<TestReportGroupReportPO> oldGroupReports, String data) {
|
||||
for (TestReportPointPO point : oldPoints) {
|
||||
testReportStorageService.deleteQuietly(point.getExcelStoragePath());
|
||||
testReportStorageService.deleteQuietly(point.getWordStoragePath());
|
||||
}
|
||||
for (TestReportGroupReportPO groupReport : oldGroupReports) {
|
||||
testReportStorageService.deleteQuietly(groupReport.getReportStoragePath());
|
||||
}
|
||||
testReportStorageService.deleteQuietly(resolveSummaryStoragePath(data));
|
||||
}
|
||||
|
||||
private void removeOldRows(String reportId) {
|
||||
LambdaQueryWrapper<TestReportPointPO> pointWrapper = new LambdaQueryWrapper<TestReportPointPO>();
|
||||
pointWrapper.eq(TestReportPointPO::getReportId, reportId);
|
||||
testReportPointMapper.delete(pointWrapper);
|
||||
|
||||
LambdaQueryWrapper<TestReportGroupReportPO> groupReportWrapper = new LambdaQueryWrapper<TestReportGroupReportPO>();
|
||||
groupReportWrapper.eq(TestReportGroupReportPO::getReportId, reportId);
|
||||
testReportGroupReportMapper.delete(groupReportWrapper);
|
||||
}
|
||||
|
||||
private String buildSnapshotJson(TestReportLedgerExcelParser.ParsedLedger ledger, String summaryStoragePath,
|
||||
BaseDataSummary summary, int groupCount) {
|
||||
ObjectNode root = OBJECT_MAPPER.createObjectNode();
|
||||
root.put("version", "2.0");
|
||||
root.put("importMode", "summary-excel-with-base-data");
|
||||
root.put("summaryFileName", ledger.getSummaryFileName());
|
||||
root.put("summaryStoragePath", summaryStoragePath);
|
||||
|
||||
ObjectNode sheetSummary = root.putObject("sheetSummary");
|
||||
sheetSummary.put("pointSheet", TestReportConst.LEDGER_POINT_SHEET_NAME);
|
||||
sheetSummary.put("deviceSheetPresent", !ledger.getDevices().isEmpty());
|
||||
sheetSummary.put("clientSheetPresent", !ledger.getClients().isEmpty());
|
||||
|
||||
root.put("rowCount", ledger.getPoints().size());
|
||||
root.put("pointCount", ledger.getPoints().size());
|
||||
root.put("groupCount", groupCount);
|
||||
|
||||
ArrayNode groupSummary = root.putArray("groupSummary");
|
||||
Map<Integer, Long> groupCounter = ledger.getPoints().stream()
|
||||
.collect(Collectors.groupingBy(TestReportLedgerExcelParser.ParsedPointRow::getGroupNo,
|
||||
LinkedHashMap::new, Collectors.counting()));
|
||||
for (Map.Entry<Integer, Long> entry : groupCounter.entrySet()) {
|
||||
ObjectNode groupNode = groupSummary.addObject();
|
||||
if (entry.getKey() == null) {
|
||||
groupNode.putNull("groupNo");
|
||||
} else {
|
||||
groupNode.put("groupNo", entry.getKey());
|
||||
}
|
||||
groupNode.put("count", entry.getValue());
|
||||
}
|
||||
|
||||
ObjectNode baseDataSummary = root.putObject("baseDataSummary");
|
||||
baseDataSummary.put("deviceAddedCount", summary.getDeviceAddedCount());
|
||||
baseDataSummary.put("deviceReuseCount", summary.getDeviceReuseCount());
|
||||
baseDataSummary.put("clientAddedCount", summary.getClientAddedCount());
|
||||
baseDataSummary.put("clientReuseCount", summary.getClientReuseCount());
|
||||
return root.toString();
|
||||
}
|
||||
|
||||
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.setTempStoragePath(tempStoragePath);
|
||||
result.setUploadedFileCount(uploadedFileCount);
|
||||
result.setTotalFileCount(uploadedFileCount);
|
||||
result.setPointCount(ledger.getPoints().size());
|
||||
result.setExcelAttachmentCount(ledger.getPoints().size());
|
||||
result.setWordAttachmentCount(ledger.getPoints().size());
|
||||
result.setDeviceAddedCount(summary.getDeviceAddedCount());
|
||||
result.setDeviceReuseCount(summary.getDeviceReuseCount());
|
||||
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("完成", "导入完成"));
|
||||
return result;
|
||||
}
|
||||
|
||||
private TestReportLedgerImportStageVO buildStage(String stage, String message) {
|
||||
TestReportLedgerImportStageVO stageVO = new TestReportLedgerImportStageVO();
|
||||
stageVO.setStage(stage);
|
||||
stageVO.setSuccess(Boolean.TRUE);
|
||||
stageVO.setMessage(message);
|
||||
return stageVO;
|
||||
}
|
||||
|
||||
private String resolveSummaryStoragePath(String data) {
|
||||
if (StrUtil.isBlank(data)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
String summaryStoragePath = OBJECT_MAPPER.readTree(data).path("summaryStoragePath").asText();
|
||||
return StrUtil.isBlank(summaryStoragePath) ? null : summaryStoragePath;
|
||||
} catch (Exception exception) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private List<TestReportPointPO> loadNormalPoints(String reportId) {
|
||||
LambdaQueryWrapper<TestReportPointPO> wrapper = new LambdaQueryWrapper<TestReportPointPO>();
|
||||
wrapper.eq(TestReportPointPO::getReportId, reportId)
|
||||
.eq(TestReportPointPO::getDeleted, TestReportConst.DELETED_NO);
|
||||
return testReportPointMapper.selectList(wrapper);
|
||||
}
|
||||
|
||||
private List<TestReportGroupReportPO> loadNormalGroupReports(String reportId) {
|
||||
LambdaQueryWrapper<TestReportGroupReportPO> wrapper = new LambdaQueryWrapper<TestReportGroupReportPO>();
|
||||
wrapper.eq(TestReportGroupReportPO::getReportId, reportId)
|
||||
.eq(TestReportGroupReportPO::getDeleted, TestReportConst.DELETED_NO);
|
||||
return testReportGroupReportMapper.selectList(wrapper);
|
||||
}
|
||||
|
||||
private Map<String, TestDevicePO> loadExistingDevices(Collection<String> deviceNos) {
|
||||
if (deviceNos.isEmpty()) {
|
||||
return new HashMap<String, TestDevicePO>();
|
||||
}
|
||||
LambdaQueryWrapper<TestDevicePO> wrapper = new LambdaQueryWrapper<TestDevicePO>();
|
||||
wrapper.in(TestDevicePO::getNo, deviceNos)
|
||||
.eq(TestDevicePO::getStatus, TestDeviceConst.STATUS_NORMAL);
|
||||
return testDeviceMapper.selectList(wrapper).stream()
|
||||
.collect(Collectors.toMap(TestDevicePO::getNo, item -> item, (first, second) -> first, HashMap::new));
|
||||
}
|
||||
|
||||
private Map<String, ClientUnitPO> loadExistingClients(Collection<String> clientNames) {
|
||||
if (clientNames.isEmpty()) {
|
||||
return new HashMap<String, ClientUnitPO>();
|
||||
}
|
||||
LambdaQueryWrapper<ClientUnitPO> wrapper = new LambdaQueryWrapper<ClientUnitPO>();
|
||||
wrapper.in(ClientUnitPO::getName, clientNames)
|
||||
.eq(ClientUnitPO::getStatus, ClientUnitConst.STATUS_NORMAL);
|
||||
return clientUnitMapper.selectList(wrapper).stream()
|
||||
.collect(Collectors.toMap(ClientUnitPO::getName, item -> item, (first, second) -> first, HashMap::new));
|
||||
}
|
||||
|
||||
private Collection<String> collectDeviceNos(TestReportLedgerExcelParser.ParsedLedger ledger) {
|
||||
Set<String> deviceNos = new HashSet<String>();
|
||||
for (TestReportLedgerExcelParser.ParsedPointRow point : ledger.getPoints()) {
|
||||
if (StrUtil.isNotBlank(point.getTestDeviceNo())) {
|
||||
deviceNos.add(point.getTestDeviceNo());
|
||||
}
|
||||
}
|
||||
for (TestReportLedgerExcelParser.ParsedDeviceRow device : ledger.getDevices()) {
|
||||
deviceNos.add(device.getNo());
|
||||
}
|
||||
return deviceNos;
|
||||
}
|
||||
|
||||
private Collection<String> collectClientNames(TestReportLedgerExcelParser.ParsedLedger ledger) {
|
||||
Set<String> clientNames = new HashSet<String>();
|
||||
for (TestReportLedgerExcelParser.ParsedPointRow point : ledger.getPoints()) {
|
||||
if (StrUtil.isNotBlank(point.getClientUnitName())) {
|
||||
clientNames.add(point.getClientUnitName());
|
||||
}
|
||||
}
|
||||
for (TestReportLedgerExcelParser.ParsedClientRow client : ledger.getClients()) {
|
||||
clientNames.add(client.getName());
|
||||
}
|
||||
return clientNames;
|
||||
}
|
||||
|
||||
private Map<String, String> loadDeviceTypeNameIdMap() {
|
||||
List<TestDeviceTypeNameVO> typeRows = testDeviceMapper.selectNormalDeviceTypes();
|
||||
if (typeRows == null || typeRows.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<String, String> result = new HashMap<String, String>();
|
||||
for (TestDeviceTypeNameVO typeRow : typeRows) {
|
||||
if (typeRow != null && StrUtil.isNotBlank(typeRow.getName()) && StrUtil.isNotBlank(typeRow.getId())) {
|
||||
result.put(typeRow.getName(), typeRow.getId());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String resolveDeviceState(String stateName, Integer rowNo) {
|
||||
String normalizedState = normalizeFileName(stateName);
|
||||
if ("01".equals(normalizedState) || "在运".equals(normalizedState)) {
|
||||
return TestDeviceConst.STATE_RUNNING;
|
||||
}
|
||||
if ("02".equals(normalizedState) || "退运".equals(normalizedState)) {
|
||||
return TestDeviceConst.STATE_RETIRED;
|
||||
}
|
||||
throw new BusinessException(CommonResponseEnum.FAIL,
|
||||
"基础信息维护阶段:检测设备sheet第" + rowNo + "行设备状态不正确");
|
||||
}
|
||||
|
||||
private String defaultEmpty(String value) {
|
||||
return value == null ? "" : value;
|
||||
}
|
||||
|
||||
private String normalizeFileName(String fileName) {
|
||||
String normalized = fileName == null ? null : fileName.trim();
|
||||
return StrUtil.isBlank(normalized) ? null : normalized;
|
||||
}
|
||||
|
||||
private static class BaseDataSummary {
|
||||
private int deviceAddedCount;
|
||||
private int deviceReuseCount;
|
||||
private int clientAddedCount;
|
||||
private int clientReuseCount;
|
||||
|
||||
public int getDeviceAddedCount() {
|
||||
return deviceAddedCount;
|
||||
}
|
||||
|
||||
public void setDeviceAddedCount(int deviceAddedCount) {
|
||||
this.deviceAddedCount = deviceAddedCount;
|
||||
}
|
||||
|
||||
public int getDeviceReuseCount() {
|
||||
return deviceReuseCount;
|
||||
}
|
||||
|
||||
public void setDeviceReuseCount(int deviceReuseCount) {
|
||||
this.deviceReuseCount = deviceReuseCount;
|
||||
}
|
||||
|
||||
public int getClientAddedCount() {
|
||||
return clientAddedCount;
|
||||
}
|
||||
|
||||
public void setClientAddedCount(int clientAddedCount) {
|
||||
this.clientAddedCount = clientAddedCount;
|
||||
}
|
||||
|
||||
public int getClientReuseCount() {
|
||||
return clientReuseCount;
|
||||
}
|
||||
|
||||
public void setClientReuseCount(int clientReuseCount) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.njcn.gather.aireport.testreport.component;
|
||||
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.aireport.testreport.pojo.constant.TestReportConst;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.CellStyle;
|
||||
import org.apache.poi.ss.usermodel.Font;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.xssf.usermodel.XSSFSheet;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class TestReportLedgerTemplateService {
|
||||
|
||||
public byte[] buildTemplate() {
|
||||
try (XSSFWorkbook workbook = new XSSFWorkbook();
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||
writeGuideSheet(workbook.createSheet(TestReportConst.LEDGER_GUIDE_SHEET_NAME));
|
||||
writeHeaders(workbook, workbook.createSheet(TestReportConst.LEDGER_POINT_SHEET_NAME),
|
||||
Arrays.asList("分组号", "变电站", "监测点名称", "检测设备编号", "委托单位名称", "Excel附件名称", "Word附件名称"));
|
||||
writeHeaders(workbook, workbook.createSheet(TestReportConst.LEDGER_DEVICE_SHEET_NAME),
|
||||
Arrays.asList("设备型号", "设备编号", "标准有效期", "设备状态", "标准报告文件名"));
|
||||
writeHeaders(workbook, workbook.createSheet(TestReportConst.LEDGER_CLIENT_SHEET_NAME),
|
||||
Arrays.asList("单位名称", "联系人", "联系方式", "地址"));
|
||||
workbook.write(outputStream);
|
||||
return outputStream.toByteArray();
|
||||
} catch (IOException exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "下载模板失败");
|
||||
}
|
||||
}
|
||||
|
||||
private void writeGuideSheet(XSSFSheet sheet) {
|
||||
Row row0 = sheet.createRow(0);
|
||||
row0.createCell(0).setCellValue("1. 汇总文件名固定为台账信息汇总.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附件名称");
|
||||
Row row3 = sheet.createRow(3);
|
||||
row3.createCell(0).setCellValue("4. 检测设备编号、委托单位名称优先匹配数据库,未命中时再匹配本次sheet");
|
||||
sheet.setColumnWidth(0, 120 * 256);
|
||||
}
|
||||
|
||||
private void writeHeaders(XSSFWorkbook workbook, XSSFSheet sheet, List<String> headers) {
|
||||
CellStyle style = workbook.createCellStyle();
|
||||
Font font = workbook.createFont();
|
||||
font.setBold(true);
|
||||
style.setFont(font);
|
||||
Row headerRow = sheet.createRow(0);
|
||||
for (int i = 0; i < headers.size(); i++) {
|
||||
Cell cell = headerRow.createCell(i);
|
||||
cell.setCellValue(headers.get(i));
|
||||
cell.setCellStyle(style);
|
||||
sheet.setColumnWidth(i, 24 * 256);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.njcn.gather.aireport.testreport.component;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import lombok.Data;
|
||||
import org.apache.poi.ss.usermodel.DataFormatter;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.usermodel.WorkbookFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class TestReportPointExcelParser {
|
||||
|
||||
private static final String NODE_INFO_SHEET_NAME = "节点信息";
|
||||
|
||||
public ParsedPoint parse(String fileName, InputStream inputStream) {
|
||||
try (Workbook workbook = WorkbookFactory.create(inputStream)) {
|
||||
Sheet sheet = workbook.getSheet(NODE_INFO_SHEET_NAME);
|
||||
if (sheet == null) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "原始Excel缺少节点信息sheet");
|
||||
}
|
||||
return parseNodeInfoSheet(fileName, sheet);
|
||||
} catch (BusinessException exception) {
|
||||
throw exception;
|
||||
} catch (IOException exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "读取原始Excel失败");
|
||||
} catch (Exception exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "解析原始Excel失败");
|
||||
}
|
||||
}
|
||||
|
||||
private ParsedPoint parseNodeInfoSheet(String fileName, Sheet sheet) {
|
||||
Map<String, String> keyValueMap = new LinkedHashMap<String, String>();
|
||||
DataFormatter formatter = new DataFormatter();
|
||||
for (Row row : sheet) {
|
||||
short lastCellNum = row.getLastCellNum();
|
||||
if (lastCellNum < 2) {
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < lastCellNum - 1; i++) {
|
||||
String key = trim(formatter.formatCellValue(row.getCell(i)));
|
||||
String value = trim(formatter.formatCellValue(row.getCell(i + 1)));
|
||||
if (StrUtil.isNotBlank(key) && StrUtil.isNotBlank(value) && !keyValueMap.containsKey(key)) {
|
||||
keyValueMap.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParsedPoint point = new ParsedPoint();
|
||||
point.setExcelAttachmentName(fileName);
|
||||
point.setSubstationName(requireAny(keyValueMap, "变电站", "厂站名称", "变电站名称"));
|
||||
point.setPointName(requireAny(keyValueMap, "监测点名称", "节点名称", "监测点"));
|
||||
point.setMonitorTime(optionalAny(keyValueMap, "监测时间", "录波时间", "开始时间"));
|
||||
point.setVoltageLevel(optionalAny(keyValueMap, "电压等级", "电压"));
|
||||
point.setPtRatio(optionalAny(keyValueMap, "PT变比", "PT"));
|
||||
point.setCtRatio(optionalAny(keyValueMap, "CT变比", "CT"));
|
||||
point.setBaseShortCircuitCapacity(optionalAny(keyValueMap, "基准短路容量"));
|
||||
point.setMinShortCircuitCapacity(optionalAny(keyValueMap, "最小短路容量"));
|
||||
point.setProtocolCapacity(optionalAny(keyValueMap, "用户协议容量"));
|
||||
point.setPowerSupplyCapacity(optionalAny(keyValueMap, "供电设备容量"));
|
||||
return point;
|
||||
}
|
||||
|
||||
private String requireAny(Map<String, String> keyValueMap, String... candidateKeys) {
|
||||
String value = optionalAny(keyValueMap, candidateKeys);
|
||||
if (StrUtil.isBlank(value)) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "节点信息sheet缺少必要字段");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private String optionalAny(Map<String, String> keyValueMap, String... candidateKeys) {
|
||||
for (String candidateKey : candidateKeys) {
|
||||
String value = keyValueMap.get(candidateKey);
|
||||
if (StrUtil.isNotBlank(value)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String trim(String value) {
|
||||
return StrUtil.trimToNull(value);
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ParsedPoint {
|
||||
private String excelAttachmentName;
|
||||
private String substationName;
|
||||
private String pointName;
|
||||
private String monitorTime;
|
||||
private String voltageLevel;
|
||||
private String ptRatio;
|
||||
private String ctRatio;
|
||||
private String baseShortCircuitCapacity;
|
||||
private String minShortCircuitCapacity;
|
||||
private String protocolCapacity;
|
||||
private String powerSupplyCapacity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,357 @@
|
||||
package com.njcn.gather.aireport.testreport.component;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.aireport.testreport.pojo.constant.TestReportConst;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
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
|
||||
public class TestReportStorageService {
|
||||
|
||||
@Value("${files.path}")
|
||||
private String filesRootPath;
|
||||
|
||||
public String saveSourceExcel(String reportId, MultipartFile file) {
|
||||
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, "淇濆瓨鍙拌处姹囨€籈xcel澶辫触");
|
||||
}
|
||||
|
||||
public String savePointExcelAttachment(String reportId, MultipartFile file) {
|
||||
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, "淇濆瓨鐩戞祴鐐筗ord闄勪欢澶辫触");
|
||||
}
|
||||
|
||||
public UploadedBatch saveTempBatchFiles(String reportId, String batchId, 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 = 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));
|
||||
}
|
||||
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) {
|
||||
Path targetPath = resolveReportDir(reportId, TestReportConst.STORAGE_GENERATED_REPORT_DIR).resolve(fileName);
|
||||
try {
|
||||
Files.createDirectories(targetPath.getParent());
|
||||
Files.write(targetPath, content);
|
||||
} catch (IOException exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "淇濆瓨鐢熸垚鎶ュ憡澶辫触");
|
||||
}
|
||||
return buildRelativePath(reportId, TestReportConst.STORAGE_GENERATED_REPORT_DIR, fileName);
|
||||
}
|
||||
|
||||
public Path resolveBusinessPath(String relativePath) {
|
||||
return Paths.get(filesRootPath, TestReportConst.STORAGE_DIR).resolve(relativePath).normalize();
|
||||
}
|
||||
|
||||
public void deleteQuietly(String relativePath) {
|
||||
if (StrUtil.isBlank(relativePath)) {
|
||||
return;
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
private Path resolveReportDir(String reportId, String childDir) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,12 @@ import com.njcn.common.pojo.constant.OperateType;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.response.HttpResult;
|
||||
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;
|
||||
import com.njcn.web.controller.BaseController;
|
||||
@@ -23,7 +28,9 @@ import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -92,6 +99,13 @@ public class TestReportController extends BaseController {
|
||||
return testReportService.previewTemplate(id);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("导出台账导入模板")
|
||||
@GetMapping("/ledger/template")
|
||||
public ResponseEntity<byte[]> downloadLedgerTemplate() {
|
||||
return testReportService.downloadLedgerTemplate();
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("导出普测报告")
|
||||
@GetMapping("/export")
|
||||
@@ -108,4 +122,75 @@ public class TestReportController extends BaseController {
|
||||
return HttpResultUtil.assembleCommonResponseResult(result ? CommonResponseEnum.SUCCESS : CommonResponseEnum.FAIL,
|
||||
result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE)
|
||||
@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,
|
||||
@RequestBody @Validated TestReportParam.LedgerImportParam param) {
|
||||
String methodDescribe = getMethodDescribe("importLedger");
|
||||
TestReportLedgerImportResultVO result = testReportService.importLedger(id, param);
|
||||
return HttpResultUtil.assembleCommonResponseResult(Boolean.TRUE.equals(result.getSuccess())
|
||||
? CommonResponseEnum.SUCCESS : CommonResponseEnum.FAIL,
|
||||
result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("查询监测点列表")
|
||||
@GetMapping("/{id}/point/list")
|
||||
public HttpResult<List<TestReportPointVO>> listPoints(@PathVariable("id") String id) {
|
||||
String methodDescribe = getMethodDescribe("listPoints");
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS,
|
||||
testReportService.listPoints(id), methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE)
|
||||
@ApiOperation("保存监测点分组")
|
||||
@PostMapping("/{id}/group/save")
|
||||
public HttpResult<Boolean> saveGroups(@PathVariable("id") String id,
|
||||
@RequestBody @Validated TestReportGroupSaveParam param) {
|
||||
String methodDescribe = getMethodDescribe("saveGroups");
|
||||
boolean result = testReportService.saveGroups(id, param);
|
||||
return HttpResultUtil.assembleCommonResponseResult(result ? CommonResponseEnum.SUCCESS : CommonResponseEnum.FAIL,
|
||||
result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("查询分组报告结果")
|
||||
@GetMapping("/{id}/group-report/list")
|
||||
public HttpResult<List<TestReportGroupReportVO>> listGroupReports(@PathVariable("id") String id) {
|
||||
String methodDescribe = getMethodDescribe("listGroupReports");
|
||||
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS,
|
||||
testReportService.listGroupReports(id), methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON, operateType = OperateType.UPDATE)
|
||||
@ApiOperation("按分组生成报告")
|
||||
@PostMapping("/{id}/generate")
|
||||
public HttpResult<Boolean> generateGroupedReports(@PathVariable("id") String id) {
|
||||
String methodDescribe = getMethodDescribe("generateGroupedReports");
|
||||
boolean result = testReportService.generateGroupedReports(id);
|
||||
return HttpResultUtil.assembleCommonResponseResult(result ? CommonResponseEnum.SUCCESS : CommonResponseEnum.FAIL,
|
||||
result, methodDescribe);
|
||||
}
|
||||
|
||||
@OperateInfo(info = LogEnum.BUSINESS_COMMON)
|
||||
@ApiOperation("下载分组报告")
|
||||
@ApiImplicitParam(name = "groupReportId", value = "分组报告结果ID", required = true)
|
||||
@GetMapping("/group-report/{groupReportId}/download")
|
||||
public ResponseEntity<byte[]> downloadGroupReport(@PathVariable("groupReportId") String groupReportId) {
|
||||
return testReportService.downloadGroupReport(groupReportId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.njcn.gather.aireport.testreport.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.njcn.gather.aireport.testreport.pojo.po.TestReportGroupReportPO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface TestReportGroupReportMapper extends BaseMapper<TestReportGroupReportPO> {
|
||||
}
|
||||
@@ -18,6 +18,9 @@ public interface TestReportMapper extends BaseMapper<TestReportPO> {
|
||||
"r.report_model_id as reportModelId, rm.name as reportModelName, r.create_unit as createUnit,",
|
||||
"r.contract_number as contractNumber, r.standard, r.data, r.phonenumber, r.check_id as checkId,",
|
||||
"checker.name as checkerName, r.check_time as checkTime, r.check_result as checkResult, r.state,",
|
||||
"r.report_generate_state as reportGenerateState,",
|
||||
"(select count(1) from ai_testreport_point p where p.report_id = r.id and p.deleted = 0) as pointCount,",
|
||||
"(select count(1) from ai_testreport_group_report gr where gr.report_id = r.id and gr.deleted = 0) as groupCount,",
|
||||
"r.status, r.create_by as createBy, creator.name as createByName, r.create_time as createTime,",
|
||||
"r.update_by as updateBy, updater.name as updateByName, r.update_time as updateTime",
|
||||
"from ai_testreport r",
|
||||
@@ -52,6 +55,9 @@ public interface TestReportMapper extends BaseMapper<TestReportPO> {
|
||||
"r.report_model_id as reportModelId, rm.name as reportModelName, r.create_unit as createUnit,",
|
||||
"r.contract_number as contractNumber, r.standard, r.data, r.phonenumber, r.check_id as checkId,",
|
||||
"checker.name as checkerName, r.check_time as checkTime, r.check_result as checkResult, r.state,",
|
||||
"r.report_generate_state as reportGenerateState,",
|
||||
"(select count(1) from ai_testreport_point p where p.report_id = r.id and p.deleted = 0) as pointCount,",
|
||||
"(select count(1) from ai_testreport_group_report gr where gr.report_id = r.id and gr.deleted = 0) as groupCount,",
|
||||
"r.status, r.create_by as createBy, creator.name as createByName, r.create_time as createTime,",
|
||||
"r.update_by as updateBy, updater.name as updateByName, r.update_time as updateTime",
|
||||
"from ai_testreport r",
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.njcn.gather.aireport.testreport.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.njcn.gather.aireport.testreport.pojo.po.TestReportPointPO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface TestReportPointMapper extends BaseMapper<TestReportPointPO> {
|
||||
}
|
||||
@@ -11,6 +11,36 @@ public final class TestReportConst {
|
||||
public static final Integer STATUS_DELETED = 0;
|
||||
public static final Integer STATUS_NORMAL = 1;
|
||||
|
||||
public static final Integer REPORT_GENERATE_STATE_PENDING = 0;
|
||||
public static final Integer REPORT_GENERATE_STATE_GENERATING = 1;
|
||||
public static final Integer REPORT_GENERATE_STATE_FINISHED = 2;
|
||||
|
||||
public static final Integer GROUP_REPORT_GENERATE_STATE_PENDING = 0;
|
||||
public static final Integer GROUP_REPORT_GENERATE_STATE_GENERATING = 1;
|
||||
public static final Integer GROUP_REPORT_GENERATE_STATE_SUCCESS = 2;
|
||||
public static final Integer GROUP_REPORT_GENERATE_STATE_FAILED = 3;
|
||||
|
||||
public static final Integer DELETED_NO = 0;
|
||||
public static final Integer DELETED_YES = 1;
|
||||
|
||||
public static final String STORAGE_DIR = "test-report";
|
||||
public static final String STORAGE_SOURCE_EXCEL_DIR = "source-excel";
|
||||
public static final String STORAGE_LEDGER_SUMMARY_DIR = "ledger-summary";
|
||||
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 = "填写说明";
|
||||
public static final String LEDGER_POINT_SHEET_NAME = "监测点信息";
|
||||
public static final String LEDGER_DEVICE_SHEET_NAME = "检测设备";
|
||||
public static final String LEDGER_CLIENT_SHEET_NAME = "委托单位";
|
||||
|
||||
private TestReportConst() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.njcn.gather.aireport.testreport.pojo.param;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class TestReportGroupSaveParam {
|
||||
|
||||
@ApiModelProperty("分组列表")
|
||||
@Valid
|
||||
@NotEmpty(message = "分组列表不能为空")
|
||||
private List<GroupItem> groups;
|
||||
|
||||
@Data
|
||||
public static class GroupItem {
|
||||
@ApiModelProperty("组号")
|
||||
@NotNull(message = "组号不能为空")
|
||||
private Integer groupNo;
|
||||
|
||||
@ApiModelProperty("监测点ID列表")
|
||||
@NotEmpty(message = "监测点列表不能为空")
|
||||
private List<String> pointIds;
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,9 @@ public class TestReportParam {
|
||||
|
||||
@Data
|
||||
public static class AddParam {
|
||||
@ApiModelProperty("\u666e\u6d4b\u62a5\u544aID\uff0c\u4e0d\u4f20\u65f6\u7531\u540e\u7aef\u751f\u6210")
|
||||
private String id;
|
||||
|
||||
@ApiModelProperty("\u666e\u6d4b\u62a5\u544a\u7f16\u53f7")
|
||||
@NotBlank(message = "\u666e\u6d4b\u62a5\u544a\u7f16\u53f7\u4e0d\u80fd\u4e3a\u7a7a")
|
||||
@Size(max = 32, message = "\u666e\u6d4b\u62a5\u544a\u7f16\u53f7\u4e0d\u80fd\u8d85\u8fc732\u4e2a\u5b57\u7b26")
|
||||
@@ -80,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.njcn.gather.aireport.testreport.pojo.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("ai_testreport_group_report")
|
||||
public class TestReportGroupReportPO {
|
||||
|
||||
@TableId(type = IdType.INPUT)
|
||||
private String id;
|
||||
private String reportId;
|
||||
private Integer groupNo;
|
||||
private String reportName;
|
||||
private String reportFileName;
|
||||
private String reportStoragePath;
|
||||
private Integer generateState;
|
||||
private String generateMessage;
|
||||
private LocalDateTime generateTime;
|
||||
private Integer sortNo;
|
||||
private Integer deleted;
|
||||
private String createBy;
|
||||
private LocalDateTime createTime;
|
||||
private String updateBy;
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -30,6 +30,8 @@ public class TestReportPO implements Serializable {
|
||||
private String standard;
|
||||
@TableField("data")
|
||||
private String data;
|
||||
@TableField("report_generate_state")
|
||||
private Integer reportGenerateState;
|
||||
@TableField("phonenumber")
|
||||
private String phonenumber;
|
||||
@TableField("state")
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.njcn.gather.aireport.testreport.pojo.po;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@TableName("ai_testreport_point")
|
||||
public class TestReportPointPO {
|
||||
|
||||
@TableId(type = IdType.INPUT)
|
||||
private String id;
|
||||
private String reportId;
|
||||
private Integer groupNo;
|
||||
private String substationName;
|
||||
private String deviceName;
|
||||
private String pointName;
|
||||
private String testDeviceNo;
|
||||
private String clientUnitName;
|
||||
private String monitorTime;
|
||||
private String voltageLevel;
|
||||
private String ptRatio;
|
||||
private String ctRatio;
|
||||
private String baseShortCircuitCapacity;
|
||||
private String minShortCircuitCapacity;
|
||||
private String protocolCapacity;
|
||||
private String powerSupplyCapacity;
|
||||
private String excelAttachmentName;
|
||||
private String excelStoragePath;
|
||||
private String wordAttachmentName;
|
||||
private String wordStoragePath;
|
||||
private Integer sortNo;
|
||||
private Integer deleted;
|
||||
private String createBy;
|
||||
private LocalDateTime createTime;
|
||||
private String updateBy;
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.njcn.gather.aireport.testreport.pojo.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class TestReportGroupReportVO {
|
||||
|
||||
private String id;
|
||||
private Integer groupNo;
|
||||
private String reportName;
|
||||
private String reportFileName;
|
||||
private String reportStoragePath;
|
||||
private Integer generateState;
|
||||
private String generateMessage;
|
||||
private LocalDateTime generateTime;
|
||||
private Integer sortNo;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.njcn.gather.aireport.testreport.pojo.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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;
|
||||
private Integer deviceAddedCount;
|
||||
private Integer deviceReuseCount;
|
||||
private Integer clientAddedCount;
|
||||
private Integer clientReuseCount;
|
||||
private Integer groupCount;
|
||||
private List<String> failReasons = new ArrayList<String>();
|
||||
private List<TestReportLedgerImportStageVO> stages = new ArrayList<TestReportLedgerImportStageVO>();
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.njcn.gather.aireport.testreport.pojo.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TestReportLedgerImportStageVO {
|
||||
|
||||
private String stage;
|
||||
private Boolean success;
|
||||
private String message;
|
||||
}
|
||||
@@ -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>();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.njcn.gather.aireport.testreport.pojo.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class TestReportPointImportResultVO {
|
||||
|
||||
private Integer fileCount;
|
||||
private Integer pointCount;
|
||||
private List<String> failReasons = new ArrayList<String>();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.njcn.gather.aireport.testreport.pojo.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TestReportPointVO {
|
||||
|
||||
private String id;
|
||||
private Integer groupNo;
|
||||
private String substationName;
|
||||
private String deviceName;
|
||||
private String pointName;
|
||||
private String testDeviceNo;
|
||||
private String clientUnitName;
|
||||
private String monitorTime;
|
||||
private String voltageLevel;
|
||||
private String ptRatio;
|
||||
private String ctRatio;
|
||||
private String baseShortCircuitCapacity;
|
||||
private String minShortCircuitCapacity;
|
||||
private String protocolCapacity;
|
||||
private String powerSupplyCapacity;
|
||||
private String excelAttachmentName;
|
||||
private String wordAttachmentName;
|
||||
}
|
||||
@@ -53,6 +53,12 @@ public class TestReportVO {
|
||||
private String checkResult;
|
||||
@ApiModelProperty("\u62a5\u544a\u72b6\u6001")
|
||||
private String state;
|
||||
@ApiModelProperty("\u62a5\u544a\u751f\u6210\u72b6\u6001")
|
||||
private Integer reportGenerateState;
|
||||
@ApiModelProperty("\u76d1\u6d4b\u70b9\u6570\u91cf")
|
||||
private Integer pointCount;
|
||||
@ApiModelProperty("\u5206\u7ec4\u6570\u91cf")
|
||||
private Integer groupCount;
|
||||
@ApiModelProperty("\u903b\u8f91\u72b6\u6001")
|
||||
private Integer status;
|
||||
@ApiModelProperty("\u521b\u5efa\u4eba\u663e\u793a\u540d")
|
||||
|
||||
@@ -2,10 +2,16 @@ package com.njcn.gather.aireport.testreport.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
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.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;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -28,4 +34,20 @@ public interface TestReportService extends IService<TestReportPO> {
|
||||
void exportTestReports(TestReportParam.QueryParam param);
|
||||
|
||||
ResponseEntity<byte[]> previewTemplate(String id);
|
||||
|
||||
TestReportLedgerValidateResultVO validateLedger(String id, MultipartFile[] files);
|
||||
|
||||
TestReportLedgerImportResultVO importLedger(String id, TestReportParam.LedgerImportParam param);
|
||||
|
||||
ResponseEntity<byte[]> downloadLedgerTemplate();
|
||||
|
||||
List<TestReportPointVO> listPoints(String id);
|
||||
|
||||
boolean saveGroups(String id, TestReportGroupSaveParam param);
|
||||
|
||||
List<TestReportGroupReportVO> listGroupReports(String id);
|
||||
|
||||
boolean generateGroupedReports(String id);
|
||||
|
||||
ResponseEntity<byte[]> downloadGroupReport(String groupReportId);
|
||||
}
|
||||
|
||||
@@ -6,17 +6,31 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.aireport.reportmodel.component.ReportModelFileStorageService;
|
||||
import com.njcn.gather.aireport.reportmodel.pojo.po.ReportModelPO;
|
||||
import com.njcn.gather.aireport.reportmodel.service.ReportModelService;
|
||||
import com.njcn.gather.aireport.testreport.component.TestReportPointExcelParser;
|
||||
import com.njcn.gather.aireport.testreport.component.TestReportLedgerImportService;
|
||||
import com.njcn.gather.aireport.testreport.component.TestReportLedgerTemplateService;
|
||||
import com.njcn.gather.aireport.testreport.component.TestReportStorageService;
|
||||
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.constant.TestReportConst;
|
||||
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.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.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;
|
||||
import com.njcn.gather.comservice.filepreview.FilePreviewService;
|
||||
@@ -30,12 +44,15 @@ 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;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
@@ -49,10 +66,13 @@ import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
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");
|
||||
@@ -88,23 +108,51 @@ public class TestReportServiceImpl extends ServiceImpl<TestReportMapper, TestRep
|
||||
private static final String MSG_PREVIEW_NOT_READY = "\u62a5\u544a\u6a21\u677f\u9884\u89c8\u670d\u52a1\u672a\u521d\u59cb\u5316";
|
||||
private static final String MSG_TEMPLATE_FILE_NOT_FOUND = "\u62a5\u544a\u6a21\u677f\u6587\u4ef6\u4e0d\u5b58\u5728";
|
||||
private static final String MSG_ENCODE_FILE_NAME_FAILED = "\u7f16\u7801\u62a5\u544a\u6a21\u677f\u6587\u4ef6\u540d\u5931\u8d25";
|
||||
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";
|
||||
private static final String MSG_GROUP_NO_INVALID = "\u7ec4\u53f7\u5fc5\u987b\u4e3a\u6b63\u6574\u6570";
|
||||
private static final String MSG_GROUP_REPORT_NOT_FOUND = "\u5206\u7ec4\u62a5\u544a\u4e0d\u5b58\u5728\u6216\u5df2\u5220\u9664";
|
||||
private static final String MSG_GROUPS_NOT_READY = "\u8bf7\u5148\u5b8c\u6210\u76d1\u6d4b\u70b9\u5206\u7ec4";
|
||||
|
||||
private final ReportModelService reportModelService;
|
||||
private final ReportModelFileStorageService reportModelFileStorageService;
|
||||
private final FilePreviewService filePreviewService;
|
||||
private final IDictTypeService dictTypeService;
|
||||
private final IDictDataService dictDataService;
|
||||
private final TestReportPointMapper testReportPointMapper;
|
||||
private final TestReportGroupReportMapper testReportGroupReportMapper;
|
||||
private final TestReportPointExcelParser testReportPointExcelParser;
|
||||
private final TestReportStorageService testReportStorageService;
|
||||
private final TestReportLedgerImportService testReportLedgerImportService;
|
||||
private final TestReportLedgerTemplateService testReportLedgerTemplateService;
|
||||
|
||||
public TestReportServiceImpl(ReportModelService reportModelService,
|
||||
ReportModelFileStorageService reportModelFileStorageService,
|
||||
FilePreviewService filePreviewService,
|
||||
IDictTypeService dictTypeService,
|
||||
IDictDataService dictDataService) {
|
||||
IDictDataService dictDataService,
|
||||
TestReportPointMapper testReportPointMapper,
|
||||
TestReportGroupReportMapper testReportGroupReportMapper,
|
||||
TestReportPointExcelParser testReportPointExcelParser,
|
||||
TestReportStorageService testReportStorageService,
|
||||
TestReportLedgerImportService testReportLedgerImportService,
|
||||
TestReportLedgerTemplateService testReportLedgerTemplateService) {
|
||||
this.reportModelService = reportModelService;
|
||||
this.reportModelFileStorageService = reportModelFileStorageService;
|
||||
this.filePreviewService = filePreviewService;
|
||||
this.dictTypeService = dictTypeService;
|
||||
this.dictDataService = dictDataService;
|
||||
this.testReportPointMapper = testReportPointMapper;
|
||||
this.testReportGroupReportMapper = testReportGroupReportMapper;
|
||||
this.testReportPointExcelParser = testReportPointExcelParser;
|
||||
this.testReportStorageService = testReportStorageService;
|
||||
this.testReportLedgerImportService = testReportLedgerImportService;
|
||||
this.testReportLedgerTemplateService = testReportLedgerTemplateService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -130,9 +178,10 @@ public class TestReportServiceImpl extends ServiceImpl<TestReportMapper, TestRep
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
String userId = resolveCurrentUserId();
|
||||
TestReportPO report = new TestReportPO();
|
||||
report.setId(UUID.randomUUID().toString());
|
||||
report.setId(resolveAddReportId(param));
|
||||
applyBusinessFields(report, data);
|
||||
report.setState(TestReportConst.STATE_EDITING);
|
||||
report.setReportGenerateState(TestReportConst.REPORT_GENERATE_STATE_PENDING);
|
||||
report.setStatus(TestReportConst.STATUS_NORMAL);
|
||||
report.setCreateBy(userId);
|
||||
report.setCreateTime(now);
|
||||
@@ -200,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;
|
||||
@@ -254,6 +311,121 @@ public class TestReportServiceImpl extends ServiceImpl<TestReportMapper, TestRep
|
||||
.body(previewContent.getContent());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
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);
|
||||
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
|
||||
public ResponseEntity<byte[]> downloadLedgerTemplate() {
|
||||
byte[] content = testReportLedgerTemplateService.buildTemplate();
|
||||
return ResponseEntity.ok()
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename*=UTF-8''" + encodeFileName(TestReportConst.LEDGER_SUMMARY_FILE_NAME))
|
||||
.contentType(MediaType.parseMediaType(
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"))
|
||||
.body(content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TestReportPointVO> listPoints(String id) {
|
||||
requireNormal(id);
|
||||
return loadNormalPoints(id).stream().map(this::toPointVO).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean saveGroups(String id, TestReportGroupSaveParam param) {
|
||||
TestReportPO report = requireNormal(id);
|
||||
ensureImportable(report);
|
||||
List<TestReportPointPO> points = loadNormalPoints(id);
|
||||
validateGroupingPayload(points, param);
|
||||
String userId = resolveCurrentUserId();
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
Map<String, Integer> pointGroupMap = buildPointGroupMap(param);
|
||||
for (TestReportPointPO point : points) {
|
||||
point.setGroupNo(pointGroupMap.get(point.getId()));
|
||||
point.setUpdateBy(userId);
|
||||
point.setUpdateTime(now);
|
||||
testReportPointMapper.updateById(point);
|
||||
}
|
||||
rebuildGroupReports(report, param, userId, now);
|
||||
report.setData(updateImportSnapshotGroupCount(report.getData(), param.getGroups().size()));
|
||||
report.setUpdateBy(userId);
|
||||
report.setUpdateTime(now);
|
||||
return this.updateById(report);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TestReportGroupReportVO> listGroupReports(String id) {
|
||||
requireNormal(id);
|
||||
return loadNormalGroupReports(id).stream().map(this::toGroupReportVO).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean generateGroupedReports(String id) {
|
||||
TestReportPO report = requireNormal(id);
|
||||
ensureImportable(report);
|
||||
List<TestReportGroupReportPO> groupReports = loadNormalGroupReports(id);
|
||||
if (groupReports.isEmpty()) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, MSG_GROUPS_NOT_READY);
|
||||
}
|
||||
report.setReportGenerateState(TestReportConst.REPORT_GENERATE_STATE_GENERATING);
|
||||
report.setUpdateBy(resolveCurrentUserId());
|
||||
report.setUpdateTime(LocalDateTime.now());
|
||||
this.updateById(report);
|
||||
try {
|
||||
for (TestReportGroupReportPO groupReport : groupReports) {
|
||||
generateSingleGroupReport(report, groupReport);
|
||||
}
|
||||
report.setReportGenerateState(TestReportConst.REPORT_GENERATE_STATE_FINISHED);
|
||||
report.setUpdateTime(LocalDateTime.now());
|
||||
return this.updateById(report);
|
||||
} catch (RuntimeException exception) {
|
||||
report.setReportGenerateState(TestReportConst.REPORT_GENERATE_STATE_PENDING);
|
||||
report.setUpdateTime(LocalDateTime.now());
|
||||
this.updateById(report);
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<byte[]> downloadGroupReport(String groupReportId) {
|
||||
TestReportGroupReportPO groupReport = requireNormalGroupReport(groupReportId);
|
||||
if (StrUtil.isBlank(groupReport.getReportStoragePath())) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, MSG_TEMPLATE_FILE_NOT_FOUND);
|
||||
}
|
||||
try {
|
||||
Path filePath = testReportStorageService.resolveBusinessPath(groupReport.getReportStoragePath());
|
||||
byte[] bytes = Files.readAllBytes(filePath);
|
||||
return ResponseEntity.ok()
|
||||
.header(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename*=UTF-8''" + encodeFileName(groupReport.getReportFileName()))
|
||||
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||
.body(bytes);
|
||||
} catch (Exception exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, MSG_TEMPLATE_FILE_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
private TestReportParam.QueryParam normalizeQueryParam(TestReportParam.QueryParam param) {
|
||||
TestReportParam.QueryParam query = param == null ? new TestReportParam.QueryParam() : param;
|
||||
query.setKeyword(trimToNull(query.getKeyword()));
|
||||
@@ -296,10 +468,320 @@ public class TestReportServiceImpl extends ServiceImpl<TestReportMapper, TestRep
|
||||
report.setPhonenumber(data.getPhonenumber());
|
||||
}
|
||||
|
||||
private String resolveAddReportId(TestReportParam.AddParam param) {
|
||||
String frontendId = param == null ? null : trimToNull(param.getId());
|
||||
return frontendId == null ? UUID.randomUUID().toString() : frontendId;
|
||||
}
|
||||
|
||||
private boolean allowEdit(String state) {
|
||||
return TestReportConst.STATE_EDITING.equals(state) || TestReportConst.STATE_REJECTED.equals(state);
|
||||
}
|
||||
|
||||
private void ensureImportable(TestReportPO report) {
|
||||
Integer reportGenerateState = report.getReportGenerateState();
|
||||
if (reportGenerateState != null && !TestReportConst.REPORT_GENERATE_STATE_PENDING.equals(reportGenerateState)) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, MSG_ONLY_PENDING_CAN_IMPORT);
|
||||
}
|
||||
}
|
||||
|
||||
private List<MultipartFile> normalizeImportFiles(MultipartFile[] files) {
|
||||
if (files == null || files.length == 0) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, MSG_IMPORT_FILES_REQUIRED);
|
||||
}
|
||||
List<MultipartFile> result = new ArrayList<MultipartFile>();
|
||||
Set<String> fileNames = new HashSet<String>();
|
||||
for (MultipartFile file : files) {
|
||||
if (file == null || file.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
String originalFileName = trimToNull(file.getOriginalFilename());
|
||||
if (originalFileName == null) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, MSG_IMPORT_FILES_REQUIRED);
|
||||
}
|
||||
String normalizedFileName = originalFileName.toLowerCase();
|
||||
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);
|
||||
}
|
||||
result.add(file);
|
||||
}
|
||||
if (result.isEmpty()) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, MSG_IMPORT_FILES_REQUIRED);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<TestReportPointExcelParser.ParsedPoint> parseAllPoints(List<MultipartFile> files) {
|
||||
List<TestReportPointExcelParser.ParsedPoint> result = new ArrayList<TestReportPointExcelParser.ParsedPoint>();
|
||||
for (MultipartFile file : files) {
|
||||
try {
|
||||
result.add(testReportPointExcelParser.parse(file.getOriginalFilename(), file.getInputStream()));
|
||||
} catch (IOException exception) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, "读取原始Excel失败");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void overwriteReportPoints(TestReportPO report, List<MultipartFile> files,
|
||||
List<TestReportPointExcelParser.ParsedPoint> parsedPoints) {
|
||||
List<TestReportPointPO> oldPoints = loadNormalPoints(report.getId());
|
||||
List<TestReportGroupReportPO> oldGroupReports = loadNormalGroupReports(report.getId());
|
||||
deletePointFiles(oldPoints);
|
||||
deleteGroupReportFiles(oldGroupReports);
|
||||
removeOldRows(report.getId());
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
String userId = resolveCurrentUserId();
|
||||
for (int i = 0; i < files.size(); i++) {
|
||||
MultipartFile file = files.get(i);
|
||||
TestReportPointExcelParser.ParsedPoint parsedPoint = parsedPoints.get(i);
|
||||
String storagePath = testReportStorageService.saveSourceExcel(report.getId(), file);
|
||||
TestReportPointPO point = new TestReportPointPO();
|
||||
point.setId(UUID.randomUUID().toString());
|
||||
point.setReportId(report.getId());
|
||||
point.setGroupNo(null);
|
||||
point.setSubstationName(parsedPoint.getSubstationName());
|
||||
point.setPointName(parsedPoint.getPointName());
|
||||
point.setMonitorTime(parsedPoint.getMonitorTime());
|
||||
point.setVoltageLevel(parsedPoint.getVoltageLevel());
|
||||
point.setPtRatio(parsedPoint.getPtRatio());
|
||||
point.setCtRatio(parsedPoint.getCtRatio());
|
||||
point.setBaseShortCircuitCapacity(parsedPoint.getBaseShortCircuitCapacity());
|
||||
point.setMinShortCircuitCapacity(parsedPoint.getMinShortCircuitCapacity());
|
||||
point.setProtocolCapacity(parsedPoint.getProtocolCapacity());
|
||||
point.setPowerSupplyCapacity(parsedPoint.getPowerSupplyCapacity());
|
||||
point.setExcelAttachmentName(parsedPoint.getExcelAttachmentName());
|
||||
point.setExcelStoragePath(storagePath);
|
||||
point.setSortNo(i + 1);
|
||||
point.setDeleted(TestReportConst.DELETED_NO);
|
||||
point.setCreateBy(userId);
|
||||
point.setCreateTime(now);
|
||||
point.setUpdateBy(userId);
|
||||
point.setUpdateTime(now);
|
||||
testReportPointMapper.insert(point);
|
||||
}
|
||||
report.setData(buildImportSnapshotJson(parsedPoints.size(), 0));
|
||||
report.setReportGenerateState(TestReportConst.REPORT_GENERATE_STATE_PENDING);
|
||||
report.setUpdateBy(userId);
|
||||
report.setUpdateTime(now);
|
||||
this.updateById(report);
|
||||
}
|
||||
|
||||
private void deletePointFiles(List<TestReportPointPO> oldPoints) {
|
||||
for (TestReportPointPO oldPoint : oldPoints) {
|
||||
testReportStorageService.deleteQuietly(oldPoint.getExcelStoragePath());
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteGroupReportFiles(List<TestReportGroupReportPO> oldGroupReports) {
|
||||
for (TestReportGroupReportPO oldGroupReport : oldGroupReports) {
|
||||
testReportStorageService.deleteQuietly(oldGroupReport.getReportStoragePath());
|
||||
}
|
||||
}
|
||||
|
||||
private void removeOldRows(String reportId) {
|
||||
LambdaQueryWrapper<TestReportPointPO> pointWrapper = new LambdaQueryWrapper<TestReportPointPO>();
|
||||
pointWrapper.eq(TestReportPointPO::getReportId, reportId);
|
||||
testReportPointMapper.delete(pointWrapper);
|
||||
|
||||
LambdaQueryWrapper<TestReportGroupReportPO> groupReportWrapper = new LambdaQueryWrapper<TestReportGroupReportPO>();
|
||||
groupReportWrapper.eq(TestReportGroupReportPO::getReportId, reportId);
|
||||
testReportGroupReportMapper.delete(groupReportWrapper);
|
||||
}
|
||||
|
||||
private String buildImportSnapshotJson(int pointCount, int groupCount) {
|
||||
ObjectNode root = OBJECT_MAPPER.createObjectNode();
|
||||
root.put("version", "1.0");
|
||||
root.put("importMode", "multi-source-excel-auto-parse");
|
||||
root.put("fileCount", pointCount);
|
||||
root.put("pointCount", pointCount);
|
||||
root.put("groupCount", groupCount);
|
||||
return root.toString();
|
||||
}
|
||||
|
||||
private String updateImportSnapshotGroupCount(String data, int groupCount) {
|
||||
try {
|
||||
JsonNode jsonNode = StrUtil.isBlank(data) ? OBJECT_MAPPER.createObjectNode() : OBJECT_MAPPER.readTree(data);
|
||||
ObjectNode root = jsonNode instanceof ObjectNode ? (ObjectNode) jsonNode : OBJECT_MAPPER.createObjectNode();
|
||||
if (!root.has("version")) {
|
||||
root.put("version", "1.0");
|
||||
}
|
||||
if (!root.has("importMode")) {
|
||||
root.put("importMode", "multi-source-excel-auto-parse");
|
||||
}
|
||||
root.put("groupCount", groupCount);
|
||||
return root.toString();
|
||||
} catch (Exception exception) {
|
||||
return buildImportSnapshotJson(loadNormalPointsCountFromData(data), groupCount);
|
||||
}
|
||||
}
|
||||
|
||||
private int loadNormalPointsCountFromData(String data) {
|
||||
try {
|
||||
if (StrUtil.isBlank(data)) {
|
||||
return 0;
|
||||
}
|
||||
JsonNode jsonNode = OBJECT_MAPPER.readTree(data);
|
||||
JsonNode pointCountNode = jsonNode.get("pointCount");
|
||||
return pointCountNode == null ? 0 : pointCountNode.asInt(0);
|
||||
} catch (Exception exception) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private List<TestReportPointPO> loadNormalPoints(String reportId) {
|
||||
LambdaQueryWrapper<TestReportPointPO> wrapper = new LambdaQueryWrapper<TestReportPointPO>();
|
||||
wrapper.eq(TestReportPointPO::getReportId, reportId)
|
||||
.eq(TestReportPointPO::getDeleted, TestReportConst.DELETED_NO)
|
||||
.orderByAsc(TestReportPointPO::getSortNo)
|
||||
.orderByAsc(TestReportPointPO::getCreateTime);
|
||||
return testReportPointMapper.selectList(wrapper);
|
||||
}
|
||||
|
||||
private List<TestReportGroupReportPO> loadNormalGroupReports(String reportId) {
|
||||
LambdaQueryWrapper<TestReportGroupReportPO> wrapper = new LambdaQueryWrapper<TestReportGroupReportPO>();
|
||||
wrapper.eq(TestReportGroupReportPO::getReportId, reportId)
|
||||
.eq(TestReportGroupReportPO::getDeleted, TestReportConst.DELETED_NO)
|
||||
.orderByAsc(TestReportGroupReportPO::getSortNo)
|
||||
.orderByAsc(TestReportGroupReportPO::getGroupNo);
|
||||
return testReportGroupReportMapper.selectList(wrapper);
|
||||
}
|
||||
|
||||
private TestReportGroupReportPO requireNormalGroupReport(String groupReportId) {
|
||||
LambdaQueryWrapper<TestReportGroupReportPO> wrapper = new LambdaQueryWrapper<TestReportGroupReportPO>();
|
||||
wrapper.eq(TestReportGroupReportPO::getId, requireText(groupReportId, MSG_ID_REQUIRED))
|
||||
.eq(TestReportGroupReportPO::getDeleted, TestReportConst.DELETED_NO);
|
||||
TestReportGroupReportPO groupReport = testReportGroupReportMapper.selectOne(wrapper);
|
||||
if (groupReport == null) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, MSG_GROUP_REPORT_NOT_FOUND);
|
||||
}
|
||||
return groupReport;
|
||||
}
|
||||
|
||||
private void validateGroupingPayload(List<TestReportPointPO> points, TestReportGroupSaveParam param) {
|
||||
if (param == null || param.getGroups() == null || param.getGroups().isEmpty()) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, MSG_GROUPS_REQUIRED);
|
||||
}
|
||||
Set<String> pointIds = points.stream().map(TestReportPointPO::getId).collect(Collectors.toSet());
|
||||
Set<String> payloadPointIds = new HashSet<String>();
|
||||
for (TestReportGroupSaveParam.GroupItem group : param.getGroups()) {
|
||||
if (group.getGroupNo() == null || group.getGroupNo() <= 0) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, MSG_GROUP_NO_INVALID);
|
||||
}
|
||||
for (String pointId : group.getPointIds()) {
|
||||
if (!payloadPointIds.add(pointId)) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, MSG_GROUP_POINT_DUPLICATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!payloadPointIds.equals(pointIds)) {
|
||||
throw new BusinessException(CommonResponseEnum.FAIL, MSG_GROUP_POINT_MISSING);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Integer> buildPointGroupMap(TestReportGroupSaveParam param) {
|
||||
Map<String, Integer> result = new LinkedHashMap<String, Integer>();
|
||||
for (TestReportGroupSaveParam.GroupItem group : param.getGroups()) {
|
||||
for (String pointId : group.getPointIds()) {
|
||||
result.put(pointId, group.getGroupNo());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void rebuildGroupReports(TestReportPO report, TestReportGroupSaveParam param, String userId, LocalDateTime now) {
|
||||
deleteGroupReportFiles(loadNormalGroupReports(report.getId()));
|
||||
LambdaQueryWrapper<TestReportGroupReportPO> wrapper = new LambdaQueryWrapper<TestReportGroupReportPO>();
|
||||
wrapper.eq(TestReportGroupReportPO::getReportId, report.getId());
|
||||
testReportGroupReportMapper.delete(wrapper);
|
||||
int sortNo = 1;
|
||||
for (TestReportGroupSaveParam.GroupItem group : param.getGroups()) {
|
||||
TestReportGroupReportPO groupReport = new TestReportGroupReportPO();
|
||||
groupReport.setId(UUID.randomUUID().toString());
|
||||
groupReport.setReportId(report.getId());
|
||||
groupReport.setGroupNo(group.getGroupNo());
|
||||
groupReport.setGenerateState(TestReportConst.GROUP_REPORT_GENERATE_STATE_PENDING);
|
||||
groupReport.setSortNo(sortNo++);
|
||||
groupReport.setDeleted(TestReportConst.DELETED_NO);
|
||||
groupReport.setCreateBy(userId);
|
||||
groupReport.setCreateTime(now);
|
||||
groupReport.setUpdateBy(userId);
|
||||
groupReport.setUpdateTime(now);
|
||||
testReportGroupReportMapper.insert(groupReport);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateSingleGroupReport(TestReportPO report, TestReportGroupReportPO groupReport) {
|
||||
try {
|
||||
groupReport.setGenerateState(TestReportConst.GROUP_REPORT_GENERATE_STATE_GENERATING);
|
||||
groupReport.setGenerateMessage(null);
|
||||
groupReport.setUpdateBy(resolveCurrentUserId());
|
||||
groupReport.setUpdateTime(LocalDateTime.now());
|
||||
testReportGroupReportMapper.updateById(groupReport);
|
||||
|
||||
String reportName = report.getNo() + "-第" + groupReport.getGroupNo() + "组检测报告";
|
||||
String reportFileName = ExportFileNameUtil.appendToday(reportName + ".docx");
|
||||
String relativePath = testReportStorageService.saveGeneratedPlaceholder(report.getId(), reportFileName,
|
||||
("group:" + groupReport.getGroupNo()).getBytes(StandardCharsets.UTF_8));
|
||||
groupReport.setReportName(reportName);
|
||||
groupReport.setReportFileName(reportFileName);
|
||||
groupReport.setReportStoragePath(relativePath);
|
||||
groupReport.setGenerateState(TestReportConst.GROUP_REPORT_GENERATE_STATE_SUCCESS);
|
||||
groupReport.setGenerateTime(LocalDateTime.now());
|
||||
groupReport.setUpdateTime(LocalDateTime.now());
|
||||
testReportGroupReportMapper.updateById(groupReport);
|
||||
} catch (RuntimeException exception) {
|
||||
groupReport.setGenerateState(TestReportConst.GROUP_REPORT_GENERATE_STATE_FAILED);
|
||||
groupReport.setGenerateMessage(exception.getMessage());
|
||||
groupReport.setUpdateTime(LocalDateTime.now());
|
||||
testReportGroupReportMapper.updateById(groupReport);
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
private TestReportPointVO toPointVO(TestReportPointPO point) {
|
||||
TestReportPointVO vo = new TestReportPointVO();
|
||||
vo.setId(point.getId());
|
||||
vo.setGroupNo(point.getGroupNo());
|
||||
vo.setSubstationName(defaultDash(point.getSubstationName()));
|
||||
vo.setDeviceName(defaultDash(point.getDeviceName()));
|
||||
vo.setPointName(defaultDash(point.getPointName()));
|
||||
vo.setTestDeviceNo(defaultDash(point.getTestDeviceNo()));
|
||||
vo.setClientUnitName(defaultDash(point.getClientUnitName()));
|
||||
vo.setMonitorTime(defaultDash(point.getMonitorTime()));
|
||||
vo.setVoltageLevel(defaultDash(point.getVoltageLevel()));
|
||||
vo.setPtRatio(defaultDash(point.getPtRatio()));
|
||||
vo.setCtRatio(defaultDash(point.getCtRatio()));
|
||||
vo.setBaseShortCircuitCapacity(defaultDash(point.getBaseShortCircuitCapacity()));
|
||||
vo.setMinShortCircuitCapacity(defaultDash(point.getMinShortCircuitCapacity()));
|
||||
vo.setProtocolCapacity(defaultDash(point.getProtocolCapacity()));
|
||||
vo.setPowerSupplyCapacity(defaultDash(point.getPowerSupplyCapacity()));
|
||||
vo.setExcelAttachmentName(defaultDash(point.getExcelAttachmentName()));
|
||||
vo.setWordAttachmentName(defaultDash(point.getWordAttachmentName()));
|
||||
return vo;
|
||||
}
|
||||
|
||||
private TestReportGroupReportVO toGroupReportVO(TestReportGroupReportPO groupReport) {
|
||||
TestReportGroupReportVO vo = new TestReportGroupReportVO();
|
||||
vo.setId(groupReport.getId());
|
||||
vo.setGroupNo(groupReport.getGroupNo());
|
||||
vo.setReportName(defaultDash(groupReport.getReportName()));
|
||||
vo.setReportFileName(defaultDash(groupReport.getReportFileName()));
|
||||
vo.setReportStoragePath(defaultDash(groupReport.getReportStoragePath()));
|
||||
vo.setGenerateState(groupReport.getGenerateState());
|
||||
vo.setGenerateMessage(defaultDash(groupReport.getGenerateMessage()));
|
||||
vo.setGenerateTime(groupReport.getGenerateTime());
|
||||
vo.setSortNo(groupReport.getSortNo());
|
||||
return vo;
|
||||
}
|
||||
|
||||
private String defaultDash(String value) {
|
||||
return StrUtil.isBlank(value) ? "-" : value;
|
||||
}
|
||||
|
||||
private void checkNoUnique(String no, String excludeId) {
|
||||
LambdaQueryWrapper<TestReportPO> wrapper = new LambdaQueryWrapper<TestReportPO>();
|
||||
wrapper.eq(TestReportPO::getNo, no)
|
||||
@@ -588,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;
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.njcn.gather.aireport.testreport.mapper.TestReportGroupReportMapper">
|
||||
</mapper>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.njcn.gather.aireport.testreport.mapper.TestReportPointMapper">
|
||||
</mapper>
|
||||
@@ -0,0 +1,153 @@
|
||||
package com.njcn.gather.aireport.testreport.component;
|
||||
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.aireport.testreport.pojo.constant.TestReportConst;
|
||||
import org.apache.poi.xssf.usermodel.XSSFSheet;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
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.io.ByteArrayOutputStream;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
class TestReportLedgerExcelParserTest {
|
||||
|
||||
@Test
|
||||
void shouldParseValidLedgerWorkbook() throws Exception {
|
||||
TestReportLedgerExcelParser parser = new TestReportLedgerExcelParser();
|
||||
MockMultipartFile summaryFile = new MockMultipartFile("files", TestReportConst.LEDGER_SUMMARY_FILE_NAME,
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", buildWorkbookBytes(true));
|
||||
Map<String, MultipartFile> attachments = new LinkedHashMap<String, MultipartFile>();
|
||||
attachments.put("point-a.xlsx", new MockMultipartFile("files", "point-a.xlsx", "application/octet-stream", new byte[]{1}));
|
||||
attachments.put("point-a.docx", new MockMultipartFile("files", "point-a.docx", "application/octet-stream", new byte[]{1}));
|
||||
|
||||
TestReportLedgerExcelParser.ParsedLedger ledger = parser.parse(summaryFile, attachments);
|
||||
|
||||
Assertions.assertEquals(1, ledger.getPoints().size());
|
||||
Assertions.assertEquals(1, ledger.getDevices().size());
|
||||
Assertions.assertEquals(1, ledger.getClients().size());
|
||||
Assertions.assertEquals("点位A", ledger.getPoints().get(0).getPointName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAllowSummaryFileNameWithSurroundingSpaces() throws Exception {
|
||||
TestReportLedgerExcelParser parser = new TestReportLedgerExcelParser();
|
||||
MockMultipartFile summaryFile = new MockMultipartFile("files", " " + TestReportConst.LEDGER_SUMMARY_FILE_NAME + " ",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", buildWorkbookBytes(true));
|
||||
Map<String, MultipartFile> attachments = new LinkedHashMap<String, MultipartFile>();
|
||||
attachments.put("point-a.xlsx", new MockMultipartFile("files", "point-a.xlsx", "application/octet-stream", new byte[]{1}));
|
||||
attachments.put("point-a.docx", new MockMultipartFile("files", "point-a.docx", "application/octet-stream", new byte[]{1}));
|
||||
|
||||
TestReportLedgerExcelParser.ParsedLedger ledger = parser.parse(summaryFile, attachments);
|
||||
|
||||
Assertions.assertEquals(TestReportConst.LEDGER_SUMMARY_FILE_NAME, ledger.getSummaryFileName());
|
||||
Assertions.assertEquals(1, ledger.getPoints().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectMissingPointSheet() throws Exception {
|
||||
TestReportLedgerExcelParser parser = new TestReportLedgerExcelParser();
|
||||
MockMultipartFile summaryFile = new MockMultipartFile("files", TestReportConst.LEDGER_SUMMARY_FILE_NAME,
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", buildWorkbookBytes(false));
|
||||
|
||||
BusinessException exception = Assertions.assertThrows(BusinessException.class,
|
||||
() -> parser.parse(summaryFile, new LinkedHashMap<String, MultipartFile>()));
|
||||
|
||||
Assertions.assertTrue(exception.getMessage().contains("监测点信息"));
|
||||
}
|
||||
|
||||
@Test
|
||||
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());
|
||||
Map<String, MultipartFile> attachments = new LinkedHashMap<String, MultipartFile>();
|
||||
attachments.put("point-a.xlsx", new MockMultipartFile("files", "point-a.xlsx", "application/octet-stream", new byte[]{1}));
|
||||
attachments.put("point-a.docx", new MockMultipartFile("files", "point-a.docx", "application/octet-stream", new byte[]{1}));
|
||||
attachments.put("point-b.xlsx", new MockMultipartFile("files", "point-b.xlsx", "application/octet-stream", new byte[]{1}));
|
||||
attachments.put("point-b.docx", new MockMultipartFile("files", "point-b.docx", "application/octet-stream", new byte[]{1}));
|
||||
|
||||
BusinessException exception = Assertions.assertThrows(BusinessException.class,
|
||||
() -> parser.parse(summaryFile, attachments));
|
||||
|
||||
Assertions.assertTrue(exception.getMessage().contains("重复点位"));
|
||||
}
|
||||
|
||||
private byte[] buildWorkbookBytes(boolean includePointSheet) throws Exception {
|
||||
try (XSSFWorkbook workbook = new XSSFWorkbook(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||
workbook.createSheet(TestReportConst.LEDGER_GUIDE_SHEET_NAME);
|
||||
if (includePointSheet) {
|
||||
XSSFSheet pointSheet = workbook.createSheet(TestReportConst.LEDGER_POINT_SHEET_NAME);
|
||||
pointSheet.createRow(0).createCell(0).setCellValue("分组号");
|
||||
pointSheet.getRow(0).createCell(1).setCellValue("变电站");
|
||||
pointSheet.getRow(0).createCell(2).setCellValue("监测点名称");
|
||||
pointSheet.getRow(0).createCell(3).setCellValue("检测设备编号");
|
||||
pointSheet.getRow(0).createCell(4).setCellValue("委托单位名称");
|
||||
pointSheet.getRow(0).createCell(5).setCellValue("Excel附件名称");
|
||||
pointSheet.getRow(0).createCell(6).setCellValue("Word附件名称");
|
||||
pointSheet.createRow(1).createCell(0).setCellValue("1");
|
||||
pointSheet.getRow(1).createCell(1).setCellValue("站点A");
|
||||
pointSheet.getRow(1).createCell(2).setCellValue("点位A");
|
||||
pointSheet.getRow(1).createCell(3).setCellValue("DEV-001");
|
||||
pointSheet.getRow(1).createCell(4).setCellValue("单位A");
|
||||
pointSheet.getRow(1).createCell(5).setCellValue("point-a.xlsx");
|
||||
pointSheet.getRow(1).createCell(6).setCellValue("point-a.docx");
|
||||
}
|
||||
XSSFSheet deviceSheet = workbook.createSheet(TestReportConst.LEDGER_DEVICE_SHEET_NAME);
|
||||
deviceSheet.createRow(0).createCell(0).setCellValue("设备型号");
|
||||
deviceSheet.getRow(0).createCell(1).setCellValue("设备编号");
|
||||
deviceSheet.getRow(0).createCell(2).setCellValue("标准有效期");
|
||||
deviceSheet.getRow(0).createCell(3).setCellValue("设备状态");
|
||||
deviceSheet.getRow(0).createCell(4).setCellValue("标准报告文件名");
|
||||
deviceSheet.createRow(1).createCell(0).setCellValue("型号A");
|
||||
deviceSheet.getRow(1).createCell(1).setCellValue("DEV-001");
|
||||
deviceSheet.getRow(1).createCell(2).setCellValue("2026-07-01");
|
||||
deviceSheet.getRow(1).createCell(3).setCellValue("在运");
|
||||
deviceSheet.getRow(1).createCell(4).setCellValue("report-a.pdf");
|
||||
XSSFSheet clientSheet = workbook.createSheet(TestReportConst.LEDGER_CLIENT_SHEET_NAME);
|
||||
clientSheet.createRow(0).createCell(0).setCellValue("单位名称");
|
||||
clientSheet.getRow(0).createCell(1).setCellValue("联系人");
|
||||
clientSheet.getRow(0).createCell(2).setCellValue("联系方式");
|
||||
clientSheet.getRow(0).createCell(3).setCellValue("地址");
|
||||
clientSheet.createRow(1).createCell(0).setCellValue("单位A");
|
||||
clientSheet.getRow(1).createCell(1).setCellValue("张三");
|
||||
clientSheet.getRow(1).createCell(2).setCellValue("13800000000");
|
||||
clientSheet.getRow(1).createCell(3).setCellValue("南京");
|
||||
workbook.write(outputStream);
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] buildDuplicatePointWorkbookBytes() throws Exception {
|
||||
try (XSSFWorkbook workbook = new XSSFWorkbook(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||
workbook.createSheet(TestReportConst.LEDGER_GUIDE_SHEET_NAME);
|
||||
XSSFSheet pointSheet = workbook.createSheet(TestReportConst.LEDGER_POINT_SHEET_NAME);
|
||||
pointSheet.createRow(0).createCell(0).setCellValue("分组号");
|
||||
pointSheet.getRow(0).createCell(1).setCellValue("变电站");
|
||||
pointSheet.getRow(0).createCell(2).setCellValue("监测点名称");
|
||||
pointSheet.getRow(0).createCell(3).setCellValue("检测设备编号");
|
||||
pointSheet.getRow(0).createCell(4).setCellValue("委托单位名称");
|
||||
pointSheet.getRow(0).createCell(5).setCellValue("Excel附件名称");
|
||||
pointSheet.getRow(0).createCell(6).setCellValue("Word附件名称");
|
||||
pointSheet.createRow(1).createCell(0).setCellValue("1");
|
||||
pointSheet.getRow(1).createCell(1).setCellValue("站点A");
|
||||
pointSheet.getRow(1).createCell(2).setCellValue("点位A");
|
||||
pointSheet.getRow(1).createCell(3).setCellValue("DEV-001");
|
||||
pointSheet.getRow(1).createCell(4).setCellValue("单位A");
|
||||
pointSheet.getRow(1).createCell(5).setCellValue("point-a.xlsx");
|
||||
pointSheet.getRow(1).createCell(6).setCellValue("point-a.docx");
|
||||
pointSheet.createRow(2).createCell(0).setCellValue("2");
|
||||
pointSheet.getRow(2).createCell(1).setCellValue("站点A");
|
||||
pointSheet.getRow(2).createCell(2).setCellValue("点位A");
|
||||
pointSheet.getRow(2).createCell(3).setCellValue("DEV-002");
|
||||
pointSheet.getRow(2).createCell(4).setCellValue("单位B");
|
||||
pointSheet.getRow(2).createCell(5).setCellValue("point-b.xlsx");
|
||||
pointSheet.getRow(2).createCell(6).setCellValue("point-b.docx");
|
||||
workbook.write(outputStream);
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
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;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
class TestReportLedgerImportServiceTest {
|
||||
|
||||
@Test
|
||||
void normalizeFileMapShouldRejectEmptyFiles() {
|
||||
TestReportLedgerImportService service = createService();
|
||||
|
||||
BusinessException exception = Assertions.assertThrows(BusinessException.class,
|
||||
() -> invokeNormalizeFileMap(service, new MultipartFile[0]));
|
||||
|
||||
Assertions.assertEquals("选择文件阶段:导入文件不能为空", exception.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveDeviceStateShouldSupportCodesAndNames() {
|
||||
TestReportLedgerImportService service = createService();
|
||||
|
||||
Assertions.assertEquals(TestDeviceConst.STATE_RUNNING, invokeResolveDeviceState(service, "01", 2));
|
||||
Assertions.assertEquals(TestDeviceConst.STATE_RUNNING, invokeResolveDeviceState(service, "在运", 2));
|
||||
Assertions.assertEquals(TestDeviceConst.STATE_RETIRED, invokeResolveDeviceState(service, "02", 3));
|
||||
Assertions.assertEquals(TestDeviceConst.STATE_RETIRED, invokeResolveDeviceState(service, "退运", 3));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveDeviceStateShouldRejectUnexpectedState() {
|
||||
TestReportLedgerImportService service = createService();
|
||||
|
||||
BusinessException exception = Assertions.assertThrows(BusinessException.class,
|
||||
() -> invokeResolveDeviceState(service, "停用", 4));
|
||||
|
||||
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() {
|
||||
return new TestReportLedgerImportService(
|
||||
mock(TestReportMapper.class),
|
||||
mock(TestReportPointMapper.class),
|
||||
mock(TestReportGroupReportMapper.class),
|
||||
mock(TestDeviceMapper.class),
|
||||
mock(ClientUnitMapper.class),
|
||||
mock(TestReportLedgerExcelParser.class),
|
||||
mock(TestReportStorageService.class));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, MultipartFile> invokeNormalizeFileMap(TestReportLedgerImportService service, MultipartFile[] files) {
|
||||
return invokePrivateMethod(service, "normalizeFileMap", new Class[]{MultipartFile[].class}, new Object[]{files});
|
||||
}
|
||||
|
||||
private String invokeResolveDeviceState(TestReportLedgerImportService service, String stateName, Integer rowNo) {
|
||||
return (String) invokePrivateMethod(service, "resolveDeviceState",
|
||||
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 {
|
||||
Method method = TestReportLedgerImportService.class.getDeclaredMethod(methodName, parameterTypes);
|
||||
method.setAccessible(true);
|
||||
return method.invoke(service, args);
|
||||
} catch (InvocationTargetException exception) {
|
||||
Throwable targetException = exception.getTargetException();
|
||||
if (targetException instanceof RuntimeException) {
|
||||
throw (RuntimeException) targetException;
|
||||
}
|
||||
throw new RuntimeException(targetException);
|
||||
} catch (Exception exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.njcn.gather.aireport.testreport.component;
|
||||
|
||||
import com.njcn.gather.aireport.testreport.pojo.constant.TestReportConst;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
class TestReportLedgerTemplateServiceTest {
|
||||
|
||||
@Test
|
||||
void shouldBuildTemplateWithRequiredSheets() throws Exception {
|
||||
TestReportLedgerTemplateService service = new TestReportLedgerTemplateService();
|
||||
|
||||
byte[] content = service.buildTemplate();
|
||||
|
||||
try (XSSFWorkbook workbook = new XSSFWorkbook(new ByteArrayInputStream(content))) {
|
||||
Assertions.assertNotNull(workbook.getSheet(TestReportConst.LEDGER_GUIDE_SHEET_NAME));
|
||||
Assertions.assertNotNull(workbook.getSheet(TestReportConst.LEDGER_POINT_SHEET_NAME));
|
||||
Assertions.assertNotNull(workbook.getSheet(TestReportConst.LEDGER_DEVICE_SHEET_NAME));
|
||||
Assertions.assertNotNull(workbook.getSheet(TestReportConst.LEDGER_CLIENT_SHEET_NAME));
|
||||
List<String> expectedHeaders = Arrays.asList(
|
||||
"分组号", "变电站", "监测点名称", "检测设备编号", "委托单位名称", "Excel附件名称", "Word附件名称");
|
||||
for (int i = 0; i < expectedHeaders.size(); i++) {
|
||||
Assertions.assertEquals(expectedHeaders.get(i),
|
||||
workbook.getSheet(TestReportConst.LEDGER_POINT_SHEET_NAME).getRow(0).getCell(i).getStringCellValue());
|
||||
}
|
||||
Assertions.assertNull(workbook.getSheet(TestReportConst.LEDGER_POINT_SHEET_NAME).getRow(0).getCell(expectedHeaders.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,20 @@ package com.njcn.gather.aireport.testreport.service.impl;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.gather.aireport.reportmodel.component.ReportModelFileStorageService;
|
||||
import com.njcn.gather.aireport.reportmodel.service.ReportModelService;
|
||||
import com.njcn.gather.aireport.testreport.component.TestReportLedgerImportService;
|
||||
import com.njcn.gather.aireport.testreport.component.TestReportLedgerTemplateService;
|
||||
import com.njcn.gather.aireport.testreport.component.TestReportPointExcelParser;
|
||||
import com.njcn.gather.aireport.testreport.component.TestReportStorageService;
|
||||
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.constant.TestReportConst;
|
||||
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;
|
||||
import com.njcn.gather.system.dictionary.pojo.po.DictData;
|
||||
@@ -15,14 +26,21 @@ import com.njcn.gather.system.dictionary.service.IDictTypeService;
|
||||
import com.njcn.gather.system.pojo.constant.DictConst;
|
||||
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.io.InputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
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 {
|
||||
@@ -30,6 +48,92 @@ class TestReportServiceImplTest {
|
||||
private static final Integer DICT_STATE_NORMAL = 1;
|
||||
private static final Integer DICT_STATE_DELETED = 0;
|
||||
|
||||
@Test
|
||||
void constantsShouldExposeGenerateStates() {
|
||||
Assertions.assertEquals(Integer.valueOf(0), TestReportConst.REPORT_GENERATE_STATE_PENDING);
|
||||
Assertions.assertEquals(Integer.valueOf(1), TestReportConst.REPORT_GENERATE_STATE_GENERATING);
|
||||
Assertions.assertEquals(Integer.valueOf(2), TestReportConst.REPORT_GENERATE_STATE_FINISHED);
|
||||
Assertions.assertEquals(Integer.valueOf(3), TestReportConst.GROUP_REPORT_GENERATE_STATE_FAILED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void pointPoShouldStoreParsedFields() {
|
||||
TestReportPointPO point = new TestReportPointPO();
|
||||
point.setSubstationName("220kV城南站");
|
||||
point.setPointName("1号监测点");
|
||||
point.setVoltageLevel("10kV");
|
||||
|
||||
Assertions.assertEquals("220kV城南站", point.getSubstationName());
|
||||
Assertions.assertEquals("1号监测点", point.getPointName());
|
||||
Assertions.assertEquals("10kV", point.getVoltageLevel());
|
||||
}
|
||||
|
||||
@Test
|
||||
void groupReportPoShouldStoreResultFields() {
|
||||
TestReportGroupReportPO groupReport = new TestReportGroupReportPO();
|
||||
groupReport.setGroupNo(1);
|
||||
groupReport.setReportName("第1组检测报告");
|
||||
groupReport.setGenerateState(TestReportConst.GROUP_REPORT_GENERATE_STATE_PENDING);
|
||||
|
||||
Assertions.assertEquals(Integer.valueOf(1), groupReport.getGroupNo());
|
||||
Assertions.assertEquals("第1组检测报告", groupReport.getReportName());
|
||||
Assertions.assertEquals(TestReportConst.GROUP_REPORT_GENERATE_STATE_PENDING, groupReport.getGenerateState());
|
||||
}
|
||||
|
||||
@Test
|
||||
void serviceShouldExposeGroupedApis() throws Exception {
|
||||
Assertions.assertNotNull(TestReportServiceImpl.class.getMethod("listTestReports", TestReportParam.QueryParam.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));
|
||||
}
|
||||
|
||||
@Test
|
||||
void pointVoShouldExposeDeviceName() throws Exception {
|
||||
Assertions.assertNotNull(TestReportPointVO.class.getMethod("getDeviceName"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void parserShouldReadNodeInfoSheetFromSampleXls() throws Exception {
|
||||
TestReportPointExcelParser parser = new TestReportPointExcelParser();
|
||||
try (InputStream inputStream = Files.newInputStream(
|
||||
Paths.get("ai-report/test-report/src/恒谊_20260610110840_202606101108_202606110957.xls"))) {
|
||||
TestReportPointExcelParser.ParsedPoint point = parser.parse(
|
||||
"恒谊_20260610110840_202606101108_202606110957.xls", inputStream);
|
||||
Assertions.assertNotNull(point.getPointName());
|
||||
Assertions.assertNotNull(point.getSubstationName());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void importLedgerShouldRejectGeneratingTask() {
|
||||
TestReportPO report = new TestReportPO();
|
||||
report.setId("report-001");
|
||||
report.setStatus(TestReportConst.STATUS_NORMAL);
|
||||
report.setReportGenerateState(TestReportConst.REPORT_GENERATE_STATE_GENERATING);
|
||||
TestableTestReportServiceImpl service = new TestableTestReportServiceImpl(mock(IDictTypeService.class),
|
||||
mock(IDictDataService.class), report);
|
||||
MockMultipartFile file = new MockMultipartFile("files", "sample.xls", "application/vnd.ms-excel", new byte[]{1});
|
||||
|
||||
BusinessException exception = Assertions.assertThrows(BusinessException.class,
|
||||
() -> 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));
|
||||
@@ -74,7 +178,33 @@ class TestReportServiceImplTest {
|
||||
BusinessException exception = Assertions.assertThrows(BusinessException.class,
|
||||
() -> service.addTestReport(param));
|
||||
|
||||
Assertions.assertTrue(exception.getMessage().contains("\u6d4b\u8bd5\u4f9d\u636e"));
|
||||
Assertions.assertTrue(exception.getMessage().contains("测试依据"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void addTestReportShouldPreferFrontendIdWhenProvided() {
|
||||
TestableAddTestReportServiceImpl service = createAddService();
|
||||
TestReportParam.AddParam param = buildValidNormalizeParam();
|
||||
param.setId("report-frontend-001");
|
||||
|
||||
boolean result = service.addTestReport(param);
|
||||
|
||||
Assertions.assertTrue(result);
|
||||
Assertions.assertNotNull(service.savedReport);
|
||||
Assertions.assertEquals("report-frontend-001", service.savedReport.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void addTestReportShouldGenerateUuidWhenFrontendIdMissing() {
|
||||
TestableAddTestReportServiceImpl service = createAddService();
|
||||
TestReportParam.AddParam param = buildValidNormalizeParam();
|
||||
|
||||
boolean result = service.addTestReport(param);
|
||||
|
||||
Assertions.assertTrue(result);
|
||||
Assertions.assertNotNull(service.savedReport);
|
||||
Assertions.assertNotNull(service.savedReport.getId());
|
||||
Assertions.assertDoesNotThrow(() -> UUID.fromString(service.savedReport.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -86,7 +216,7 @@ class TestReportServiceImplTest {
|
||||
BusinessException exception = Assertions.assertThrows(BusinessException.class,
|
||||
() -> invokeNormalizeParam(service, param));
|
||||
|
||||
Assertions.assertTrue(exception.getMessage().contains("\u6536\u8d44\u6570\u636e"));
|
||||
Assertions.assertTrue(exception.getMessage().contains("受资数据"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -98,7 +228,7 @@ class TestReportServiceImplTest {
|
||||
BusinessException exception = Assertions.assertThrows(BusinessException.class,
|
||||
() -> invokeNormalizeParam(service, param));
|
||||
|
||||
Assertions.assertTrue(exception.getMessage().contains("\u6536\u8d44\u6570\u636e"));
|
||||
Assertions.assertTrue(exception.getMessage().contains("受资数据"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -136,7 +266,7 @@ class TestReportServiceImplTest {
|
||||
BusinessException exception = Assertions.assertThrows(BusinessException.class,
|
||||
() -> service.addTestReport(param));
|
||||
|
||||
Assertions.assertTrue(exception.getMessage().contains("\u6d4b\u8bd5\u4f9d\u636e"));
|
||||
Assertions.assertTrue(exception.getMessage().contains("测试依据"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -242,7 +372,28 @@ class TestReportServiceImplTest {
|
||||
mock(ReportModelFileStorageService.class),
|
||||
mock(FilePreviewService.class),
|
||||
dictTypeService,
|
||||
dictDataService);
|
||||
dictDataService,
|
||||
mock(TestReportPointMapper.class),
|
||||
mock(TestReportGroupReportMapper.class),
|
||||
mock(TestReportPointExcelParser.class),
|
||||
mock(TestReportStorageService.class),
|
||||
mock(TestReportLedgerImportService.class),
|
||||
mock(TestReportLedgerTemplateService.class));
|
||||
}
|
||||
|
||||
private TestableAddTestReportServiceImpl createAddService() {
|
||||
IDictTypeService dictTypeService = mock(IDictTypeService.class);
|
||||
IDictDataService dictDataService = mock(IDictDataService.class);
|
||||
TestReportMapper testReportMapper = mock(TestReportMapper.class);
|
||||
mockNormalDictType(dictTypeService, DictConst.TEST_REPORT_COMPANY_DICT_CODE, "type-company");
|
||||
mockNormalDictType(dictTypeService, DictConst.TEST_REPORT_STANDARD_DICT_CODE, "type-standard");
|
||||
when(dictDataService.listDictDataByTypeId("type-company")).thenReturn(Collections.singletonList(
|
||||
buildDictData("company-1", "company-A", DICT_STATE_NORMAL)));
|
||||
when(dictDataService.listDictDataByTypeId("type-standard")).thenReturn(Collections.singletonList(
|
||||
buildDictData("std-1", "standard-A", DICT_STATE_NORMAL)));
|
||||
when(testReportMapper.countNormalClientUnit("client-1")).thenReturn(1);
|
||||
when(testReportMapper.countNormalReportModel("model-1")).thenReturn(1);
|
||||
return new TestableAddTestReportServiceImpl(dictTypeService, dictDataService, testReportMapper);
|
||||
}
|
||||
|
||||
private TestReportParam.AddParam buildValidNormalizeParam() {
|
||||
@@ -280,7 +431,10 @@ class TestReportServiceImplTest {
|
||||
private TestableTestReportServiceImpl(IDictTypeService dictTypeService, IDictDataService dictDataService,
|
||||
TestReportPO report) {
|
||||
super(mock(ReportModelService.class), mock(ReportModelFileStorageService.class),
|
||||
mock(FilePreviewService.class), dictTypeService, dictDataService);
|
||||
mock(FilePreviewService.class), dictTypeService, dictDataService,
|
||||
mock(TestReportPointMapper.class), mock(TestReportGroupReportMapper.class),
|
||||
mock(TestReportPointExcelParser.class), mock(TestReportStorageService.class),
|
||||
mock(TestReportLedgerImportService.class), mock(TestReportLedgerTemplateService.class));
|
||||
this.report = report;
|
||||
}
|
||||
|
||||
@@ -296,6 +450,31 @@ class TestReportServiceImplTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestableAddTestReportServiceImpl extends TestReportServiceImpl {
|
||||
private TestReportPO savedReport;
|
||||
|
||||
private TestableAddTestReportServiceImpl(IDictTypeService dictTypeService, IDictDataService dictDataService,
|
||||
TestReportMapper testReportMapper) {
|
||||
super(mock(ReportModelService.class), mock(ReportModelFileStorageService.class),
|
||||
mock(FilePreviewService.class), dictTypeService, dictDataService,
|
||||
mock(TestReportPointMapper.class), mock(TestReportGroupReportMapper.class),
|
||||
mock(TestReportPointExcelParser.class), mock(TestReportStorageService.class),
|
||||
mock(TestReportLedgerImportService.class), mock(TestReportLedgerTemplateService.class));
|
||||
this.baseMapper = testReportMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count(com.baomidou.mybatisplus.core.conditions.Wrapper<TestReportPO> queryWrapper) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean save(TestReportPO entity) {
|
||||
this.savedReport = entity;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void mockNormalDictType(IDictTypeService dictTypeService, String code, String typeId) {
|
||||
when(dictTypeService.getByCode(code)).thenReturn(buildDictType(typeId, code));
|
||||
}
|
||||
|
||||
302
tmp/generate_yilin_grade3_wordlist.js
Normal file
302
tmp/generate_yilin_grade3_wordlist.js
Normal file
@@ -0,0 +1,302 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const allUnits = [
|
||||
{
|
||||
title: "\u4e09\u5e74\u7ea7\u4e0a\u518c Unit 1 Hello",
|
||||
entries: [
|
||||
["hello", "/h\u0259\u02c8l\u0259\u028a/", "\u4f60\u597d"],
|
||||
["hi", "/ha\u026a/", "\u55e8\uff1b\u4f60\u597d"],
|
||||
["good morning", "/\u02cc\u0261\u028ad \u02c8m\u0254\u02d0n\u026a\u014b/", "\u65e9\u4e0a\u597d"],
|
||||
["Miss", "/m\u026as/", "\u5c0f\u59d0\uff1b\u8001\u5e08\u79f0\u547c"],
|
||||
["class", "/kl\u0251\u02d0s/", "\u540c\u5b66\u4eec\uff1b\u73ed\u7ea7"],
|
||||
["I", "/a\u026a/", "\u6211"],
|
||||
["I'm = I am", "/a\u026am/", "\u6211\u662f"]
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "\u4e09\u5e74\u7ea7\u4e0a\u518c Unit 2 I'm Liu Tao",
|
||||
entries: [
|
||||
["are", "/\u0251\u02d0(r)/", "\u662f"],
|
||||
["yes", "/jes/", "\u662f\uff1b\u5bf9"],
|
||||
["no", "/n\u0259\u028a/", "\u4e0d\uff1b\u4e0d\u662f"],
|
||||
["not", "/n\u0252t/", "\u4e0d\uff1b\u6ca1"],
|
||||
["you", "/ju\u02d0/", "\u4f60\uff1b\u4f60\u4eec"],
|
||||
["what", "/w\u0252t/", "\u4ec0\u4e48"],
|
||||
["your", "/j\u0254\u02d0(r)/", "\u4f60\u7684"]
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "\u4e09\u5e74\u7ea7\u4e0a\u518c Unit 3 My friends",
|
||||
entries: [
|
||||
["my", "/ma\u026a/", "\u6211\u7684"],
|
||||
["friend", "/frend/", "\u670b\u53cb"],
|
||||
["he", "/hi\u02d0/", "\u4ed6"],
|
||||
["she", "/\u0283i\u02d0/", "\u5979"],
|
||||
["this", "/\u00f0\u026as/", "\u8fd9\uff1b\u8fd9\u4e2a"],
|
||||
["is", "/\u026az/", "\u662f"],
|
||||
["too", "/tu\u02d0/", "\u4e5f"]
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "\u4e09\u5e74\u7ea7\u4e0a\u518c Unit 4 My family",
|
||||
entries: [
|
||||
["family", "/\u02c8f\u00e6m\u0259li/", "\u5bb6\u5ead\uff1b\u5bb6\u4eba"],
|
||||
["father", "/\u02c8f\u0251\u02d0\u00f0\u0259(r)/", "\u7236\u4eb2"],
|
||||
["mother", "/\u02c8m\u028c\u00f0\u0259(r)/", "\u6bcd\u4eb2"],
|
||||
["brother", "/\u02c8br\u028c\u00f0\u0259(r)/", "\u5144\uff1b\u5f1f"],
|
||||
["sister", "/\u02c8s\u026ast\u0259(r)/", "\u59d0\uff1b\u59b9"],
|
||||
["grandpa", "/\u02c8\u0261r\u00e6n(d)p\u0251\u02d0/", "\u7237\u7237\uff1b\u5916\u516c"],
|
||||
["grandma", "/\u02c8\u0261r\u00e6n(d)m\u0251\u02d0/", "\u5976\u5976\uff1b\u5916\u5a46"],
|
||||
["me", "/mi\u02d0/", "\u6211"]
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "\u4e09\u5e74\u7ea7\u4e0a\u518c Unit 5 Look at me",
|
||||
entries: [
|
||||
["look at", "/l\u028ak \u00e6t/", "\u770b"],
|
||||
["T-shirt", "/\u02c8ti\u02d0 \u0283\u025c\u02d0t/", "T\u6064\u886b"],
|
||||
["skirt", "/sk\u025c\u02d0t/", "\u88d9\u5b50"],
|
||||
["cap", "/k\u00e6p/", "\u5e3d\u5b50"],
|
||||
["jacket", "/\u02c8d\u0292\u00e6k\u026at/", "\u5939\u514b\u886b"],
|
||||
["nice", "/na\u026as/", "\u597d\u770b\u7684\uff1b\u597d\u7684"],
|
||||
["great", "/\u0261re\u026at/", "\u597d\u6781\u4e86"]
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "\u4e09\u5e74\u7ea7\u4e0a\u518c Unit 6 Colours",
|
||||
entries: [
|
||||
["red", "/red/", "\u7ea2\u8272"],
|
||||
["orange", "/\u02c8\u0252r\u026and\u0292/", "\u6a59\u8272"],
|
||||
["yellow", "/\u02c8jel\u0259\u028a/", "\u9ec4\u8272"],
|
||||
["green", "/\u0261ri\u02d0n/", "\u7eff\u8272"],
|
||||
["blue", "/blu\u02d0/", "\u84dd\u8272"],
|
||||
["brown", "/bra\u028an/", "\u68d5\u8272"],
|
||||
["white", "/wa\u026at/", "\u767d\u8272"],
|
||||
["black", "/bl\u00e6k/", "\u9ed1\u8272"],
|
||||
["and", "/\u00e6nd/", "\u548c"],
|
||||
["now", "/na\u028a/", "\u73b0\u5728"]
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "\u4e09\u5e74\u7ea7\u4e0a\u518c Unit 7 Would you like a pie",
|
||||
entries: [
|
||||
["would like", "/w\u028ad la\u026ak/", "\u60f3\u8981"],
|
||||
["pie", "/pa\u026a/", "\u9985\u997c"],
|
||||
["cake", "/ke\u026ak/", "\u86cb\u7cd5"],
|
||||
["egg", "/e\u0261/", "\u9e21\u86cb"],
|
||||
["sweet", "/swi\u02d0t/", "\u7cd6\u679c\uff1b\u751c\u7684"],
|
||||
["ice cream", "/\u02cca\u026as \u02c8kri\u02d0m/", "\u51b0\u6fc0\u51cc"],
|
||||
["hot dog", "/\u02c8h\u0252t d\u0252\u0261/", "\u70ed\u72d7"],
|
||||
["what about", "/w\u0252t \u0259\u02c8ba\u028at/", "\u2026\u2026\u600e\u4e48\u6837"]
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "\u4e09\u5e74\u7ea7\u4e0a\u518c Unit 8 Happy New Year",
|
||||
entries: [
|
||||
["happy", "/\u02c8h\u00e6pi/", "\u9ad8\u5174\u7684\uff1b\u5feb\u4e50\u7684"],
|
||||
["new", "/nju\u02d0/", "\u65b0\u7684"],
|
||||
["year", "/j\u026a\u0259(r)/", "\u5e74"],
|
||||
["Happy New Year", "/\u02cch\u00e6pi nju\u02d0 \u02c8j\u026a\u0259(r)/", "\u65b0\u5e74\u5feb\u4e50"],
|
||||
["uncle", "/\u02c8\u028c\u014bkl/", "\u53d4\u53d4\uff1b\u8205\u8205"],
|
||||
["aunt", "/\u0251\u02d0nt/", "\u963f\u59e8\uff1b\u59d1\u5988"],
|
||||
["doll", "/d\u0252l/", "\u73a9\u5177\u5a03\u5a03"],
|
||||
["ball", "/b\u0254\u02d0l/", "\u7403"],
|
||||
["robot", "/\u02c8r\u0259\u028ab\u0252t/", "\u673a\u5668\u4eba"],
|
||||
["toy car", "/t\u0254\u026a k\u0251\u02d0(r)/", "\u73a9\u5177\u5c0f\u6c7d\u8f66"]
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "\u4e09\u5e74\u7ea7\u4e0b\u518c Unit 1 In class",
|
||||
entries: [
|
||||
["in class", "/\u026an kl\u0251\u02d0s/", "\u5728\u4e0a\u8bfe"],
|
||||
["open", "/\u02c8\u0259\u028ap\u0259n/", "\u6253\u5f00"],
|
||||
["the", "/\u00f0\u0259/", "\u8fd9\uff1b\u90a3\uff08\u5b9a\u51a0\u8bcd\uff09"],
|
||||
["door", "/d\u0254\u02d0(r)/", "\u95e8"],
|
||||
["window", "/\u02c8w\u026and\u0259\u028a/", "\u7a97\u6237"],
|
||||
["book", "/b\u028ak/", "\u4e66"],
|
||||
["stand up", "/st\u00e6nd \u028cp/", "\u8d77\u7acb"],
|
||||
["sit down", "/s\u026at da\u028an/", "\u5750\u4e0b"],
|
||||
["please", "/pli\u02d0z/", "\u8bf7"],
|
||||
["come in", "/k\u028cm \u026an/", "\u8fdb\u6765"],
|
||||
["close", "/kl\u0259\u028az/", "\u5173\u4e0a"],
|
||||
["blackboard", "/\u02c8bl\u00e6kb\u0254\u02d0d/", "\u9ed1\u677f"]
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "\u4e09\u5e74\u7ea7\u4e0b\u518c Unit 2 In the library",
|
||||
entries: [
|
||||
["library", "/\u02c8la\u026abr\u0259ri/", "\u56fe\u4e66\u9986"],
|
||||
["shout", "/\u0283a\u028at/", "\u558a\uff1b\u53eb"],
|
||||
["eat", "/i\u02d0t/", "\u5403"],
|
||||
["run", "/r\u028cn/", "\u8dd1"],
|
||||
["talk", "/t\u0254\u02d0k/", "\u8bf4\u8bdd"],
|
||||
["sleep", "/sli\u02d0p/", "\u7761\u89c9"],
|
||||
["drink", "/dr\u026a\u014bk/", "\u559d"],
|
||||
["here", "/h\u026a\u0259(r)/", "\u8fd9\u91cc"],
|
||||
["English", "/\u02c8\u026a\u014b\u0261l\u026a\u0283/", "\u82f1\u8bed"],
|
||||
["milk", "/m\u026alk/", "\u725b\u5976"]
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "\u4e09\u5e74\u7ea7\u4e0b\u518c Unit 3 Is this your pencil",
|
||||
entries: [
|
||||
["pencil", "/\u02c8pensl/", "\u94c5\u7b14"],
|
||||
["pen", "/pen/", "\u94a2\u7b14"],
|
||||
["ruler", "/\u02c8ru\u02d0l\u0259(r)/", "\u5c3a"],
|
||||
["rubber", "/\u02c8r\u028cb\u0259(r)/", "\u6a61\u76ae"],
|
||||
["crayon", "/\u02c8kre\u026a\u0259n/", "\u8721\u7b14"],
|
||||
["schoolbag", "/\u02c8sku\u02d0lb\u00e6\u0261/", "\u4e66\u5305"],
|
||||
["pencil case", "/\u02c8pensl ke\u026as/", "\u94c5\u7b14\u76d2"],
|
||||
["lunch box", "/\u02c8l\u028cnt\u0283 b\u0252ks/", "\u5348\u9910\u76d2"],
|
||||
["where", "/we\u0259(r)/", "\u5728\u54ea\u91cc"],
|
||||
["over there", "/\u02c8\u0259\u028av\u0259(r) \u00f0e\u0259(r)/", "\u5728\u90a3\u91cc"]
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "\u4e09\u5e74\u7ea7\u4e0b\u518c Unit 4 Where's the bird",
|
||||
entries: [
|
||||
["bird", "/b\u025c\u02d0d/", "\u9e1f"],
|
||||
["under", "/\u02c8\u028cnd\u0259(r)/", "\u5728\u2026\u2026\u4e0b\u9762"],
|
||||
["behind", "/b\u026a\u02c8ha\u026and/", "\u5728\u2026\u2026\u540e\u9762"],
|
||||
["on", "/\u0252n/", "\u5728\u2026\u2026\u4e0a\u9762"],
|
||||
["desk", "/desk/", "\u4e66\u684c"],
|
||||
["chair", "/t\u0283e\u0259(r)/", "\u6905\u5b50"],
|
||||
["in", "/\u026an/", "\u5728\u2026\u2026\u91cc\u9762"],
|
||||
["tree", "/tri\u02d0/", "\u6811"],
|
||||
["beautiful", "/\u02c8bju\u02d0t\u026afl/", "\u6f02\u4eae\u7684"]
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "\u4e09\u5e74\u7ea7\u4e0b\u518c Unit 5 How old are you",
|
||||
entries: [
|
||||
["one", "/w\u028cn/", "\u4e00"],
|
||||
["two", "/tu\u02d0/", "\u4e8c"],
|
||||
["three", "/\u03b8ri\u02d0/", "\u4e09"],
|
||||
["four", "/f\u0254\u02d0(r)/", "\u56db"],
|
||||
["five", "/fa\u026av/", "\u4e94"],
|
||||
["six", "/s\u026aks/", "\u516d"],
|
||||
["seven", "/\u02c8sevn/", "\u4e03"],
|
||||
["eight", "/e\u026at/", "\u516b"],
|
||||
["nine", "/na\u026an/", "\u4e5d"],
|
||||
["ten", "/ten/", "\u5341"],
|
||||
["how old", "/ha\u028a \u0259\u028ald/", "\u51e0\u5c81"],
|
||||
["right", "/ra\u026at/", "\u5bf9\u7684\uff1b\u6b63\u786e\u7684"]
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "\u4e09\u5e74\u7ea7\u4e0b\u518c Unit 6 What time is it",
|
||||
entries: [
|
||||
["what time", "/w\u0252t ta\u026am/", "\u51e0\u70b9"],
|
||||
["wake up", "/we\u026ak \u028cp/", "\u9192\uff1b\u9192\u6765"],
|
||||
["o'clock", "/\u0259\u02c8kl\u0252k/", "\u2026\u2026\u70b9\u949f"],
|
||||
["breakfast", "/\u02c8brekf\u0259st/", "\u65e9\u9910"],
|
||||
["lunch", "/l\u028cnt\u0283/", "\u5348\u9910"],
|
||||
["dinner", "/\u02c8d\u026an\u0259(r)/", "\u665a\u9910"],
|
||||
["class", "/kl\u0251\u02d0s/", "\u8bfe\uff1b\u4e0a\u8bfe"],
|
||||
["bed", "/bed/", "\u5e8a"],
|
||||
["OK", "/\u02cc\u0259\u028a\u02c8ke\u026a/", "\u597d\uff1b\u53ef\u4ee5\u4e86"]
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "\u4e09\u5e74\u7ea7\u4e0b\u518c Unit 7 On the farm",
|
||||
entries: [
|
||||
["farm", "/f\u0251\u02d0m/", "\u519c\u573a"],
|
||||
["they", "/\u00f0e\u026a/", "\u5b83\u4eec\uff1b\u4ed6\u4eec"],
|
||||
["pig", "/p\u026a\u0261/", "\u732a"],
|
||||
["cow", "/ka\u028a/", "\u5976\u725b"],
|
||||
["chicken", "/\u02c8t\u0283\u026ak\u026an/", "\u9e21"],
|
||||
["duck", "/d\u028ck/", "\u9e2d"],
|
||||
["pear", "/pe\u0259(r)/", "\u68a8"],
|
||||
["apple", "/\u02c8\u00e6pl/", "\u82f9\u679c"],
|
||||
["orange", "/\u02c8\u0252r\u026and\u0292/", "\u6a59\u5b50"],
|
||||
["those", "/\u00f0\u0259\u028az/", "\u90a3\u4e9b"]
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "\u4e09\u5e74\u7ea7\u4e0b\u518c Unit 8 We're twins",
|
||||
entries: [
|
||||
["we", "/wi\u02d0/", "\u6211\u4eec"],
|
||||
["we're = we are", "/w\u026a\u0259(r)/", "\u6211\u4eec\u662f"],
|
||||
["twin", "/tw\u026an/", "\u53cc\u80de\u80ce\u4e4b\u4e00"],
|
||||
["man", "/m\u00e6n/", "\u7537\u4eba"],
|
||||
["woman", "/\u02c8w\u028am\u0259n/", "\u5973\u4eba"],
|
||||
["boy", "/b\u0254\u026a/", "\u7537\u5b69"],
|
||||
["girl", "/\u0261\u025c\u02d0l/", "\u5973\u5b69"],
|
||||
["baby", "/\u02c8be\u026abi/", "\u5a74\u513f"],
|
||||
["cousin", "/\u02c8k\u028czn/", "\u5802\uff08\u8868\uff09\u5144\u5f1f\u59d0\u59b9"],
|
||||
["name", "/ne\u026am/", "\u540d\u5b57"]
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
function buildHtml(title, note, units) {
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>${title}</title>
|
||||
<style>
|
||||
body { font-family: "Microsoft YaHei", "SimSun", sans-serif; margin: 28px; line-height: 1.65; }
|
||||
h1 { color: #4f378b; font-size: 20pt; margin: 0 0 12px; }
|
||||
h2 { color: #2b4c7e; font-size: 14pt; margin: 18px 0 8px; border-bottom: 1px solid #d9e2f3; padding-bottom: 4px; }
|
||||
p.note { color: #444; margin: 0 0 18px; font-size: 10.5pt; }
|
||||
table { width: 100%; border-collapse: collapse; margin-bottom: 14px; }
|
||||
td { border-bottom: 1px solid #eee; padding: 6px 4px; vertical-align: top; font-size: 11pt; }
|
||||
td.word { color: #c00000; width: 28%; font-weight: 700; }
|
||||
td.phonetic { color: #0066cc; width: 24%; }
|
||||
td.meaning { color: #008000; width: 48%; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>${title}</h1>
|
||||
<p class="note">${note}</p>
|
||||
${units.map((unit) => `
|
||||
<h2>${unit.title}</h2>
|
||||
<table>
|
||||
${unit.entries.map((entry) => `
|
||||
<tr>
|
||||
<td class="word">${entry[0]}</td>
|
||||
<td class="phonetic">${entry[1]}</td>
|
||||
<td class="meaning">${entry[2]}</td>
|
||||
</tr>
|
||||
`).join("")}
|
||||
</table>
|
||||
`).join("")}
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
function writeDoc(fileName, title, note, units) {
|
||||
const outputPath = path.join(process.cwd(), fileName);
|
||||
const html = buildHtml(title, note, units);
|
||||
fs.writeFileSync(outputPath, "\uFEFF" + html, "utf8");
|
||||
return outputPath;
|
||||
}
|
||||
|
||||
const upperUnits = allUnits.filter((unit) => unit.title.includes("\u4e09\u5e74\u7ea7\u4e0a\u518c"));
|
||||
const lowerUnits = allUnits.filter((unit) => unit.title.includes("\u4e09\u5e74\u7ea7\u4e0b\u518c"));
|
||||
|
||||
const outputs = [
|
||||
writeDoc(
|
||||
"\u6c5f\u82cf\u4e09\u5e74\u7ea7\u82f1\u8bed\u8bd1\u6797\u7248\u5355\u8bcd_\u4e0a\u4e0b\u518c_20260706.doc",
|
||||
"\u6c5f\u82cf\u4e09\u5e74\u7ea7\u82f1\u8bed\u8bd1\u6797\u7248\u5355\u8bcd\u6c47\u603b",
|
||||
"\u6574\u7406\u4e09\u5e74\u7ea7\u4e0a\u518c + \u4e0b\u518c\u3002\u82f1\u6587\u5355\u8bcd\u4e3a\u7ea2\u8272\uff0c\u97f3\u6807\u4e3a\u84dd\u8272\uff0c\u4e2d\u6587\u91ca\u4e49\u4e3a\u7eff\u8272\u3002",
|
||||
allUnits
|
||||
),
|
||||
writeDoc(
|
||||
"\u6c5f\u82cf\u4e09\u5e74\u7ea7\u82f1\u8bed\u8bd1\u6797\u7248\u4e0a\u518c\u5355\u8bcd_20260706.doc",
|
||||
"\u6c5f\u82cf\u4e09\u5e74\u7ea7\u82f1\u8bed\u8bd1\u6797\u7248\u4e0a\u518c\u5355\u8bcd",
|
||||
"\u4ec5\u5305\u542b\u4e09\u5e74\u7ea7\u4e0a\u518c\u3002\u82f1\u6587\u5355\u8bcd\u4e3a\u7ea2\u8272\uff0c\u97f3\u6807\u4e3a\u84dd\u8272\uff0c\u4e2d\u6587\u91ca\u4e49\u4e3a\u7eff\u8272\u3002",
|
||||
upperUnits
|
||||
),
|
||||
writeDoc(
|
||||
"\u6c5f\u82cf\u4e09\u5e74\u7ea7\u82f1\u8bed\u8bd1\u6797\u7248\u4e0b\u518c\u5355\u8bcd_20260706.doc",
|
||||
"\u6c5f\u82cf\u4e09\u5e74\u7ea7\u82f1\u8bed\u8bd1\u6797\u7248\u4e0b\u518c\u5355\u8bcd",
|
||||
"\u4ec5\u5305\u542b\u4e09\u5e74\u7ea7\u4e0b\u518c\u3002\u82f1\u6587\u5355\u8bcd\u4e3a\u7ea2\u8272\uff0c\u97f3\u6807\u4e3a\u84dd\u8272\uff0c\u4e2d\u6587\u91ca\u4e49\u4e3a\u7eff\u8272\u3002",
|
||||
lowerUnits
|
||||
)
|
||||
];
|
||||
|
||||
outputs.forEach((outputPath) => console.log(outputPath));
|
||||
@@ -108,6 +108,12 @@ public class JsonToXmlConversionService {
|
||||
|
||||
String iedName = mappingDocument.getIed();
|
||||
String ldPrefix = mappingDocument.getLd();
|
||||
String waveTimeFlag = mappingDocument.getWaveTimeFlag();
|
||||
if (waveTimeFlag.startsWith("U") || waveTimeFlag.startsWith("u")){
|
||||
waveTimeFlag= waveTimeFlag.toUpperCase();
|
||||
}else{
|
||||
waveTimeFlag= waveTimeFlag.toLowerCase();
|
||||
}
|
||||
|
||||
if (iedName != null && !iedName.isEmpty()) {
|
||||
xmlContent = xmlContent.replaceAll(
|
||||
@@ -123,6 +129,14 @@ public class JsonToXmlConversionService {
|
||||
);
|
||||
}
|
||||
|
||||
// 从 JSON 中读取 WaveTimeFlag 替换模板中的硬编码值
|
||||
if (waveTimeFlag != null && !waveTimeFlag.isEmpty()) {
|
||||
xmlContent = xmlContent.replaceAll(
|
||||
"<ComtradeFile\\s+WaveTimeFlag=\"[^\"]*\"",
|
||||
"<ComtradeFile WaveTimeFlag=\"" + escapeXml(waveTimeFlag) + "\""
|
||||
);
|
||||
}
|
||||
|
||||
return xmlContent;
|
||||
}
|
||||
|
||||
@@ -246,11 +260,20 @@ public class JsonToXmlConversionService {
|
||||
}else{
|
||||
sb.append(",60");
|
||||
}
|
||||
sb.append(",1");
|
||||
sb.append(",0");
|
||||
sb.append(",0");
|
||||
sb.append(",0");
|
||||
sb.append(",0");
|
||||
if(rc.getName().contains("urcb")){
|
||||
sb.append(",0");
|
||||
sb.append(",0");
|
||||
sb.append(",0");
|
||||
sb.append(",1");
|
||||
sb.append(",0");
|
||||
}else{
|
||||
sb.append(",1");
|
||||
sb.append(",0");
|
||||
sb.append(",0");
|
||||
sb.append(",0");
|
||||
sb.append(",0");
|
||||
}
|
||||
|
||||
sb.append(",yes");
|
||||
sb.append(",1");
|
||||
sb.append(",1");
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
<!--注:角型接线时,相角为0,不再进行DO、DA路径值扩展-->
|
||||
<JSConfigTemplate version="2021-12-15" author="ww" SelectStat="JiangSu" SelectReal="Kafka Producer" desc="昆明长水机场增加谐波类数据配置">
|
||||
<!--注:暂态事件解析规则配置 Flag:0-不分相 1-分相 如果Flag=0 A,B,C配置成一样,如果Flag=1,A,B,C根据实际配置-->
|
||||
<WavePhasic Flag="1" A="QVVR1" B="QVVR2" C="QVVR3" />
|
||||
<WavePhasic Flag="1" A="QVVR0" B="QVVR1" C="QVVR2" />
|
||||
<!--注:暂态事件持续事件单位:0-毫秒 1-秒-->
|
||||
<UnitOfTime Unit="0" />
|
||||
<!--注:上送值的时间:UTC-UTC时间 beijing-北京时间-->
|
||||
<ValueOfTime Unit="utc" />
|
||||
<ValueOfTime Unit="UTC" />
|
||||
<!--注:录波文件的时间:UTC-UTC时间 beijing-北京时间-->
|
||||
<ComtradeFile WaveTimeFlag="beijing" />
|
||||
<IED name="PQMonitor" desc="电能质量监测装置" />
|
||||
|
||||
Reference in New Issue
Block a user