diff --git a/entrance/src/main/resources/application.yml b/entrance/src/main/resources/application.yml index bc11383..6cf55cb 100644 --- a/entrance/src/main/resources/application.yml +++ b/entrance/src/main/resources/application.yml @@ -62,6 +62,10 @@ log: homeDir: D:\logs commonLevel: info +parse-pqdif: + storage: + path: D:\pqdif-files + activate: private-key: "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCcUyYhVqczGxblL+o/xZzF/8nf+LjrfUE/dS1aRHM7uMDD0cgCArhjtfneFePrMxt+Z7W8yNBzSarub8qsfhaVNikV7Es7oaeTygfjQXTi2n4AFkir3fM07J08RpWhl5M8f8uWTCuvFUYAw00gq55typqmnbkmJa2VIUy/iQf+cMCP7abz4/jNhUzUR3qA7TV4oMRgTdIEDUp63YF8dOC+JH8XxYrCVeHXV6fLCwmesdMzl0lB2VTEKMfLbXhOmF5g7P9y/16VCcN8UBuZlbyYfn+GAxJOSbeHi5HshOKfoSuD7Jz+3WQZpNavOWjIFExKIU38/CvnJCOP7XBCqpSTAgMBAAECggEAYeWokWRE3TpvwiOZnUpR/aVMdVi75a3ROL5XIpqPV61B+t/bU3cEpl0GF9C5pUeiRi0IoStZb3mI9D1KPW/REKyUWkhabQO1gFYbTnRlkNOn6MILzKX4cwJjDaZeeo4EBPU7N+qHyOOXrU6hdH5FfxhMdV983ajm5eeuupxER1C2kAcIklTeVpTX6EKOgZb5LBp5ssOVm2P42pOauvcRozRcvZmqnErXmukv0H4l3EVNt4rHpTn9riHUC63e8JfiYzVaF6zuNUxv6nHEft0/SRMw11XSTnNfDzcKqgjz6ksFBS/6eQQYKESk+ONC53HUuYHFAknkwsPupDCT2W8FIQKBgQDLHT/xCU3nxGr4vFKBDNaO2D5oK20ECbBO4oDvLWWmQG7f+6TsMy8PgVdMnoL4RfqGlwFAKEpS6KVFHnBVqnNEhcdy9uCI7x7Xx8UnyUtxj1EDTm76uta9Ki9OrlqB6tImDM9+Ya3vGktW37ht4WOx2OsJRhG1dbf6RLwFlH7DWwKBgQDFBxvi5I1BR6hg6Tj7xd2SqOT2Y+BED3xuSYENhWbmMhLJDResaB7mjztbxlYaY2mOE0holWm2uDmVFFhMh4jYXik4hYH8nmDzq9mDpZCZ9pyjYqnAP8THoAa8EbgrUWB8A6BPH4iL3KbMnBfBKY0pIr2xrvnjQjNBAgta7KDRKQKBgCe6oe4wxrdF2TKsC2tIqpMoQxS3Icy/ZGgZr+SYuaBKTCWtoDW/UT40K3JGMxIDBhzbXphBCUCsVt9tM8Xd4EwP6tJW7dZ7B0pnve2pVwNwaAVAiz6p2yUHIle+jN+Koe5lZRSwYIg7WW81tWpwwsJfzqFyvjYDP6hJV4mz4ROvAoGAaRcdnKvjXApomShMqJ4lTPChD3q+SA8qg3jZSOj6tZXHx00gb2kp8jg7pPvpOTIFPy6x1Ha9aCRjMk0ju84fA6lVuzwa1S907wOehUVuF3Eeo1cgy9Y3k3KbpPyeixxgpkUY4JslLdSHc2NemD0dee951qhJyRmqVOZOQDUuoeECgYEAqBw2cAFk3vM97WY06TSldGA8ajVHx3BYRjj+zl62NTQthy8fw3tqxb3c5e8toOmZWKjZvDhg2TRLhsDDQWEYg3LZG87REqVIjgEPcpjNLidjygGX8n3JF2o0O5I/EMvl0s/+LVQONfduOBvhwDqr8QNisbLsyneiAq7umewMolo=" public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnFMmIVanMxsW5S/qP8Wcxf/J3/i4631BP3UtWkRzO7jAw9HIAgK4Y7X53hXj6zMbfme1vMjQc0mq7m/KrH4WlTYpFexLO6Gnk8oH40F04tp+ABZIq93zNOydPEaVoZeTPH/LlkwrrxVGAMNNIKuebcqapp25JiWtlSFMv4kH/nDAj+2m8+P4zYVM1Ed6gO01eKDEYE3SBA1Ket2BfHTgviR/F8WKwlXh11enywsJnrHTM5dJQdlUxCjHy214TpheYOz/cv9elQnDfFAbmZW8mH5/hgMSTkm3h4uR7ITin6Erg+yc/t1kGaTWrzloyBRMSiFN/Pwr5yQjj+1wQqqUkwIDAQAB" diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/CsIcdPathParam.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/CsIcdPathParam.java index 77e65ad..4928aaa 100644 --- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/CsIcdPathParam.java +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/CsIcdPathParam.java @@ -30,6 +30,9 @@ public class CsIcdPathParam { @ApiModelProperty("ICD类型,1-手动录入的标准ICD,2-手动录入的非标准ICD,3-上游解析传递的标准ICD,4-上游解析传递的非标准ICD") private Integer type; + @ApiModelProperty("标准ICD引用ID") + private String referenceIcdId; + /** * ICD 存储记录编辑参数。 */ diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/service/impl/CsIcdPathServiceImpl.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/service/impl/CsIcdPathServiceImpl.java index a40125a..d012638 100644 --- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/service/impl/CsIcdPathServiceImpl.java +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/service/impl/CsIcdPathServiceImpl.java @@ -189,6 +189,7 @@ public class CsIcdPathServiceImpl implements CsIcdPathService { icdPath.setAngle(param.getAngle()); icdPath.setUsePhaseIndex(param.getUsePhaseIndex()); icdPath.setType(useDefaultType ? resolveIcdType(param.getType()) : param.getType()); + icdPath.setReferenceIcdId(trimToNull(param.getReferenceIcdId())); return icdPath; } diff --git a/tools/mms-mapping/src/test/java/com/njcn/gather/icd/mapping/service/impl/CsIcdPathServiceImplTest.java b/tools/mms-mapping/src/test/java/com/njcn/gather/icd/mapping/service/impl/CsIcdPathServiceImplTest.java index dae9f4f..bb45920 100644 --- a/tools/mms-mapping/src/test/java/com/njcn/gather/icd/mapping/service/impl/CsIcdPathServiceImplTest.java +++ b/tools/mms-mapping/src/test/java/com/njcn/gather/icd/mapping/service/impl/CsIcdPathServiceImplTest.java @@ -100,6 +100,20 @@ class CsIcdPathServiceImplTest { Assertions.assertArrayEquals(fileContent, captor.getValue().getIcdContent()); } + @Test + void addIcdPathShouldSaveReferenceIcdId() { + CsIcdPathParam param = buildParam("非标准ICD"); + param.setReferenceIcdId(" reference-icd "); + when(csIcdPathMapper.insert(any(CsIcdPathPO.class))).thenReturn(1); + + boolean result = service.addIcdPath(param); + + ArgumentCaptor captor = ArgumentCaptor.forClass(CsIcdPathPO.class); + verify(csIcdPathMapper).insert(captor.capture()); + Assertions.assertTrue(result); + Assertions.assertEquals("reference-icd", captor.getValue().getReferenceIcdId()); + } + @Test void addIcdPathShouldDefaultTypeToManualNonStandard() { CsIcdPathParam param = buildParam("手动录入非标准ICD"); diff --git a/tools/parse-pqdif/pom.xml b/tools/parse-pqdif/pom.xml index 8c7a5b3..ba19aa9 100644 --- a/tools/parse-pqdif/pom.xml +++ b/tools/parse-pqdif/pom.xml @@ -55,6 +55,12 @@ system ${project.basedir}/lib/pqdif-native-basic-bridge-1.0.0-jar-with-dependencies.jar + + + org.springframework.boot + spring-boot-starter-test + test + diff --git a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/component/PqdifFileStorageService.java b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/component/PqdifFileStorageService.java new file mode 100644 index 0000000..893df93 --- /dev/null +++ b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/component/PqdifFileStorageService.java @@ -0,0 +1,71 @@ +package com.njcn.gather.tool.parsepqdif.component; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.UUID; + +/** + * PQDIF 原始文件本地存储服务。 + */ +@Component +public class PqdifFileStorageService { + + private static final String DEFAULT_STORAGE_DIR = "data/parse-pqdif"; + + @Value("${parse-pqdif.storage.path:}") + private String storagePath; + + public String save(MultipartFile pqdifFile) { + if (pqdifFile == null || pqdifFile.isEmpty()) { + throw new IllegalArgumentException("PQDIF文件不能为空"); + } + try { + Path storageDir = resolveStorageDir(); + Files.createDirectories(storageDir); + if (!Files.isDirectory(storageDir)) { + throw new IllegalStateException("PQDIF文件存储路径不是目录:" + storageDir); + } + String fileName = UUID.randomUUID().toString().replace("-", "") + "_" + sanitizeFileName(pqdifFile.getOriginalFilename()); + Path target = storageDir.resolve(fileName).normalize(); + if (!target.startsWith(storageDir)) { + throw new IllegalArgumentException("PQDIF文件名不合法"); + } + try (InputStream inputStream = pqdifFile.getInputStream()) { + Files.copy(inputStream, target, StandardCopyOption.REPLACE_EXISTING); + } + return target.toString(); + } catch (Exception ex) { + throw new IllegalArgumentException("保存PQDIF文件失败:" + ex.getMessage(), ex); + } + } + + private Path resolveStorageDir() { + String configuredPath = trimToNull(storagePath); + Path path = configuredPath == null ? Paths.get(DEFAULT_STORAGE_DIR) : Paths.get(configuredPath); + return path.toAbsolutePath().normalize(); + } + + private String sanitizeFileName(String originalFilename) { + String fileName = trimToNull(originalFilename); + if (fileName == null) { + return "unnamed.pqd"; + } + String normalizedName = Paths.get(fileName).getFileName().toString(); + return normalizedName.replaceAll("[\\\\/:*?\"<>|]", "_"); + } + + private String trimToNull(String value) { + if (value == null) { + return null; + } + String text = value.trim(); + return text.isEmpty() ? null : text; + } +} diff --git a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/component/PqdifSecondParseComponent.java b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/component/PqdifSecondParseComponent.java new file mode 100644 index 0000000..ba906d5 --- /dev/null +++ b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/component/PqdifSecondParseComponent.java @@ -0,0 +1,281 @@ +package com.njcn.gather.tool.parsepqdif.component; + +import com.njcn.gather.tool.parsepqdif.pojo.vo.PqdifParseResponse; +import com.njcn.gather.tool.parsepqdif.pojo.vo.PqdifRecognizeResultVO; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 将 parse 方法返回的底层 PQDIF 解析结果转换为业务可识别结构。 + */ +@Component +public class PqdifSecondParseComponent { + + private static final String STATUS_SUCCESS = "SUCCESS"; + private static final String STATUS_FAILED = "FAILED"; + private static final String MESSAGE_SUCCESS = "PQDIF二次解析完成"; + + private static final DateTimeFormatter STANDARD_DATE_TIME_FORMATTER = + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + private static final Map PHASE_MAP = buildPhaseMap(); + private static final Map MEASUREMENT_MAP = buildMeasurementMap(); + private static final Map UNIT_MAP = buildUnitMap(); + private static final Map SERIES_BASE_TYPE_MAP = buildSeriesBaseTypeMap(); + + public PqdifRecognizeResultVO parseRecognizableData(PqdifParseResponse parseResponse) { + if (parseResponse == null) { + return failed("parse解析结果不能为空"); + } + + if (!STATUS_SUCCESS.equals(parseResponse.getStatus())) { + String message = parseResponse.getMessage() == null ? "" : parseResponse.getMessage(); + return failed("parse解析未成功,不能进行二次解析:" + message); + } + + PqdifRecognizeResultVO result = emptyResult(STATUS_SUCCESS, MESSAGE_SUCCESS); + List sourceObservations = parseResponse.getObservations(); + if (sourceObservations == null || sourceObservations.isEmpty()) { + return result; + } + + for (int observationIndex = 0; observationIndex < sourceObservations.size(); observationIndex++) { + PqdifParseResponse.ObservationVO sourceObservation = sourceObservations.get(observationIndex); + result.getObservations().add(parseObservation(sourceObservation, observationIndex, result)); + } + + result.setObservationCount((long) result.getObservations().size()); + return result; + } + + private PqdifRecognizeResultVO.RecognizedObservationVO parseObservation( + PqdifParseResponse.ObservationVO source, + int observationIndex, + PqdifRecognizeResultVO result) { + + PqdifRecognizeResultVO.RecognizedObservationVO target = new PqdifRecognizeResultVO.RecognizedObservationVO(); + target.setRecordIndex(source == null ? null : source.getRecordIndex()); + target.setName(source == null ? null : source.getName()); + target.setStartTime(source == null ? null : formatStartTime(source.getTimeStartText())); + target.setChannels(new ArrayList()); + + if (source == null || source.getChannels() == null || source.getChannels().isEmpty()) { + return target; + } + + for (int channelIndex = 0; channelIndex < source.getChannels().size(); channelIndex++) { + PqdifParseResponse.ChannelInfoVO sourceChannel = source.getChannels().get(channelIndex); + target.getChannels().add(parseChannel(sourceChannel, observationIndex, channelIndex, result)); + result.setChannelCount(result.getChannelCount() + 1); + } + + return target; + } + + private PqdifRecognizeResultVO.RecognizedChannelVO parseChannel( + PqdifParseResponse.ChannelInfoVO source, + int observationIndex, + int channelIndex, + PqdifRecognizeResultVO result) { + + PqdifRecognizeResultVO.RecognizedChannelVO target = new PqdifRecognizeResultVO.RecognizedChannelVO(); + target.setChannelIndex(source == null ? null : source.getChannelIndex()); + target.setName(source == null ? null : source.getName()); + target.setPhaseId(source == null ? null : source.getPhaseId()); + target.setQuantityMeasuredId(source == null ? null : source.getQuantityMeasuredId()); + target.setQuantityTypeGuid(source == null ? null : source.getQuantityTypeGuid()); + target.setSeries(new ArrayList()); + + if (source == null) { + return target; + } + + PhaseInfo phaseInfo = PHASE_MAP.get(source.getPhaseId()); + if (phaseInfo == null) { + addUnknown(result, channelPath(observationIndex, channelIndex), "phaseId", source.getPhaseId(), "未识别的相别ID"); + } else { + target.setPhaseCode(phaseInfo.getCode()); + target.setPhaseName(phaseInfo.getName()); + } + + String measurementName = MEASUREMENT_MAP.get(source.getQuantityMeasuredId()); + if (measurementName == null) { + addUnknown(result, channelPath(observationIndex, channelIndex), + "quantityMeasuredId", source.getQuantityMeasuredId(), "未识别的测量量ID"); + } else { + target.setMeasurementName(measurementName); + } + + if (source.getSeries() == null || source.getSeries().isEmpty()) { + return target; + } + + for (int seriesIndex = 0; seriesIndex < source.getSeries().size(); seriesIndex++) { + PqdifParseResponse.SeriesInfoVO sourceSeries = source.getSeries().get(seriesIndex); + target.getSeries().add(parseSeries(sourceSeries, observationIndex, channelIndex, seriesIndex, result)); + result.setSeriesCount(result.getSeriesCount() + 1); + } + + return target; + } + + private PqdifRecognizeResultVO.RecognizedSeriesVO parseSeries( + PqdifParseResponse.SeriesInfoVO source, + int observationIndex, + int channelIndex, + int seriesIndex, + PqdifRecognizeResultVO result) { + + PqdifRecognizeResultVO.RecognizedSeriesVO target = new PqdifRecognizeResultVO.RecognizedSeriesVO(); + if (source == null) { + return target; + } + + target.setSeriesIndex(source.getSeriesIndex()); + target.setQuantityUnitsId(source.getQuantityUnitsId()); + target.setQuantityCharacteristicGuid(source.getQuantityCharacteristicGuid()); + target.setValueTypeGuid(source.getValueTypeGuid()); + target.setSeriesBaseType(source.getSeriesBaseType()); + target.setScale(source.getScale()); + target.setOffset(source.getOffset()); + target.setDataStatus(source.getDataStatus()); + target.setDataMessage(source.getDataMessage()); + target.setValueCount(source.getValueCount()); + target.setFirstValues(source.getFirstValues()); + + String unitName = UNIT_MAP.get(source.getQuantityUnitsId()); + if (unitName == null) { + addUnknown(result, seriesPath(observationIndex, channelIndex, seriesIndex), + "quantityUnitsId", source.getQuantityUnitsId(), "未识别的单位ID"); + } else { + target.setUnitName(unitName); + } + + String dataTypeName = SERIES_BASE_TYPE_MAP.get(source.getSeriesBaseType()); + if (dataTypeName == null) { + addUnknown(result, seriesPath(observationIndex, channelIndex, seriesIndex), + "seriesBaseType", source.getSeriesBaseType(), "未识别的Series基础类型"); + } else { + target.setDataTypeName(dataTypeName); + } + + return target; + } + + private PqdifRecognizeResultVO failed(String message) { + return emptyResult(STATUS_FAILED, message); + } + + private String formatStartTime(String timeStartText) { + if (timeStartText == null || timeStartText.trim().isEmpty()) { + return timeStartText; + } + try { + return LocalDateTime.parse(timeStartText.trim()).format(STANDARD_DATE_TIME_FORMATTER); + } catch (DateTimeParseException e) { + return timeStartText; + } + } + + private PqdifRecognizeResultVO emptyResult(String status, String message) { + PqdifRecognizeResultVO result = new PqdifRecognizeResultVO(); + result.setStatus(status); + result.setMessage(message); + result.setObservationCount(0L); + result.setChannelCount(0L); + result.setSeriesCount(0L); + result.setObservations(new ArrayList()); + result.setUnknownItems(new ArrayList()); + return result; + } + + private void addUnknown(PqdifRecognizeResultVO result, String path, String fieldName, Object fieldValue, String message) { + if (fieldValue == null) { + return; + } + PqdifRecognizeResultVO.UnknownItemVO item = new PqdifRecognizeResultVO.UnknownItemVO(); + item.setPath(path); + item.setFieldName(fieldName); + item.setFieldValue(String.valueOf(fieldValue)); + item.setMessage(message); + result.getUnknownItems().add(item); + } + + private String channelPath(int observationIndex, int channelIndex) { + return "observations[" + observationIndex + "].channels[" + channelIndex + "]"; + } + + private String seriesPath(int observationIndex, int channelIndex, int seriesIndex) { + return channelPath(observationIndex, channelIndex) + ".series[" + seriesIndex + "]"; + } + + private static Map buildPhaseMap() { + Map map = new HashMap(); + map.put(0L, new PhaseInfo("UNKNOWN", "未知相别")); + map.put(1L, new PhaseInfo("A", "A相")); + map.put(2L, new PhaseInfo("B", "B相")); + map.put(3L, new PhaseInfo("C", "C相")); + map.put(4L, new PhaseInfo("N", "中性线")); + map.put(5L, new PhaseInfo("AB", "AB线电压")); + map.put(6L, new PhaseInfo("BC", "BC线电压")); + map.put(7L, new PhaseInfo("CA", "CA线电压")); + return map; + } + + private static Map buildMeasurementMap() { + Map map = new HashMap(); + map.put(0L, "电压"); + map.put(1L, "电流"); + map.put(2L, "频率"); + map.put(3L, "有功功率"); + map.put(4L, "无功功率"); + map.put(5L, "视在功率"); + map.put(6L, "功率因数"); + return map; + } + + private static Map buildUnitMap() { + Map map = new HashMap(); + map.put(27L, "A"); + map.put(29L, "V"); + map.put(32L, "Hz"); + map.put(38L, "W"); + map.put(39L, "var"); + map.put(40L, "VA"); + map.put(41L, "%"); + return map; + } + + private static Map buildSeriesBaseTypeMap() { + Map map = new HashMap(); + map.put(1L, "DOUBLE"); + map.put(2L, "INTEGER"); + map.put(3L, "BOOLEAN"); + map.put(4L, "TEXT"); + return map; + } + + private static class PhaseInfo { + private final String code; + private final String name; + + private PhaseInfo(String code, String name) { + this.code = code; + this.name = name; + } + + private String getCode() { + return code; + } + + private String getName() { + return name; + } + } +} diff --git a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/controller/CsPqdifPathController.java b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/controller/CsPqdifPathController.java index bede4a3..4fbbb68 100644 --- a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/controller/CsPqdifPathController.java +++ b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/controller/CsPqdifPathController.java @@ -6,6 +6,7 @@ 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.common.utils.LogUtil; +import com.njcn.gather.tool.parsepqdif.component.PqdifFileStorageService; import com.njcn.gather.tool.parsepqdif.pojo.param.CsPqdifPathParam; import com.njcn.gather.tool.parsepqdif.pojo.param.PqdifParseResultSaveParam; import com.njcn.gather.tool.parsepqdif.pojo.vo.CsPqdifPathDetailVO; @@ -27,7 +28,6 @@ import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; -import java.io.IOException; import java.util.List; /** @@ -42,6 +42,8 @@ public class CsPqdifPathController extends BaseController { private final CsPqdifPathService csPqdifPathService; + private final PqdifFileStorageService pqdifFileStorageService; + @OperateInfo(info = LogEnum.BUSINESS_COMMON) @ApiOperation("查询PQDIF存储记录列表") @PostMapping("/list") @@ -69,7 +71,7 @@ public class CsPqdifPathController extends BaseController { @RequestPart("request") @Validated CsPqdifPathParam param) { String methodDescribe = getMethodDescribe("addWithFile"); LogUtil.njcnDebug(log, "{},开始上传并新增PQDIF存储记录,fileName={}", methodDescribe, resolveFileName(pqdifFile)); - fillPqdifFile(param, pqdifFile); + fillPqdifFilePath(param, pqdifFile); boolean result = csPqdifPathService.addPqdifPath(param); return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); } @@ -92,7 +94,7 @@ public class CsPqdifPathController extends BaseController { String methodDescribe = getMethodDescribe("updateWithFile"); LogUtil.njcnDebug(log, "{},开始上传并编辑PQDIF存储记录,pqdifId={},fileName={}", methodDescribe, param.getId(), resolveFileName(pqdifFile)); - fillPqdifFile(param, pqdifFile); + fillPqdifFilePath(param, pqdifFile); boolean result = csPqdifPathService.updatePqdifPath(param); return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); } @@ -141,15 +143,8 @@ public class CsPqdifPathController extends BaseController { return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); } - private void fillPqdifFile(CsPqdifPathParam param, MultipartFile pqdifFile) { - if (pqdifFile == null || pqdifFile.isEmpty()) { - throw new IllegalArgumentException("PQDIF文件不能为空"); - } - try { - param.setPqdifContent(pqdifFile.getBytes()); - } catch (IOException ex) { - throw new IllegalArgumentException("读取PQDIF文件失败:" + ex.getMessage(), ex); - } + private void fillPqdifFilePath(CsPqdifPathParam param, MultipartFile pqdifFile) { + param.setFilePath(pqdifFileStorageService.save(pqdifFile)); } private String resolveFileName(MultipartFile pqdifFile) { diff --git a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/controller/ParsePqdifController.java b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/controller/ParsePqdifController.java index 4983927..5a48c85 100644 --- a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/controller/ParsePqdifController.java +++ b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/controller/ParsePqdifController.java @@ -6,6 +6,7 @@ import com.njcn.common.pojo.enums.response.CommonResponseEnum; import com.njcn.common.pojo.response.HttpResult; import com.njcn.common.utils.LogUtil; import com.njcn.gather.tool.parsepqdif.pojo.vo.PqdifParseResponse; +import com.njcn.gather.tool.parsepqdif.pojo.vo.PqdifRecognizeResultVO; import com.njcn.gather.tool.parsepqdif.service.ParsePqdifService; import com.njcn.web.controller.BaseController; import com.njcn.web.utils.HttpResultUtil; @@ -43,4 +44,17 @@ public class ParsePqdifController extends BaseController { PqdifParseResponse result = parsePqdifService.parse(pqdifFile); return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @ApiOperation("上传 PQDIF 文件并解析为可识别数据") + @ApiImplicitParam(name = "pqdifFile", value = "PQDIF文件", required = true, dataType = "__file", paramType = "form") + @PostMapping(value = "/parse-recognizable", consumes = {"multipart/form-data"}) + public HttpResult parseRecognizable(@RequestPart("pqdifFile") MultipartFile pqdifFile) { + String methodDescribe = getMethodDescribe("parseRecognizable"); + LogUtil.njcnDebug(log, "{},PQDIF二次解析入口,fileName={}", + methodDescribe, pqdifFile == null ? null : pqdifFile.getOriginalFilename()); + PqdifParseResponse parseResponse = parsePqdifService.parse(pqdifFile); + PqdifRecognizeResultVO result = parsePqdifService.parseRecognizable(parseResponse); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + } } diff --git a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/mapper/mapping/CsPqdifPathMapper.xml b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/mapper/mapping/CsPqdifPathMapper.xml index 7ca0c12..3f81fb4 100644 --- a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/mapper/mapping/CsPqdifPathMapper.xml +++ b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/mapper/mapping/CsPqdifPathMapper.xml @@ -7,7 +7,7 @@ type="com.njcn.gather.tool.parsepqdif.pojo.vo.CsPqdifPathVO"> - + @@ -26,13 +26,12 @@ SELECT ID AS id, Name AS name, - Native_Version AS nativeVersion, + File_Path AS filePath, Record_Count AS recordCount, Observation_Count AS observationCount, Sample_Value_Count AS sampleValueCount, State AS state, Result AS result, - Msg AS msg, Create_By AS createBy, Create_Time AS createTime, Update_By AS updateBy, @@ -61,8 +60,7 @@ SELECT ID AS id, Name AS name, - Json_Str AS jsonStr, - Pqdif AS pqdifContent + File_Path AS filePath FROM cs_pqdif_path WHERE ID = #{id} AND State = 1 diff --git a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/pojo/param/CsPqdifPathParam.java b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/pojo/param/CsPqdifPathParam.java index 9a24bcb..a5e8b37 100644 --- a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/pojo/param/CsPqdifPathParam.java +++ b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/pojo/param/CsPqdifPathParam.java @@ -1,5 +1,6 @@ package com.njcn.gather.tool.parsepqdif.pojo.param; +import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -18,8 +19,9 @@ public class CsPqdifPathParam { @NotBlank(message = "PQDIF名称不能为空") private String name; - @ApiModelProperty("PQDIF文件二进制内容") - private byte[] pqdifContent; + @ApiModelProperty("PQDIF原始文件存储路径") + @JsonIgnore + private String filePath; /** * PQDIF 存储记录编辑参数。 diff --git a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/pojo/param/PqdifParseResultSaveParam.java b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/pojo/param/PqdifParseResultSaveParam.java index de79c31..f1a09ee 100644 --- a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/pojo/param/PqdifParseResultSaveParam.java +++ b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/pojo/param/PqdifParseResultSaveParam.java @@ -12,9 +12,6 @@ import lombok.Data; @ApiModel("PQDIF解析结果保存参数") public class PqdifParseResultSaveParam { - @ApiModelProperty("native解析库版本") - private String nativeVersion; - @ApiModelProperty("Record总数") private Long recordCount; @@ -30,6 +27,4 @@ public class PqdifParseResultSaveParam { @ApiModelProperty("解析提示、失败原因或解析结论JSON") private JsonNode msg; - @ApiModelProperty("完整PQDIF解析结果JSON") - private String jsonStr; } diff --git a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/pojo/po/CsPqdifPathPO.java b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/pojo/po/CsPqdifPathPO.java index 0ab31cb..37ffe6c 100644 --- a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/pojo/po/CsPqdifPathPO.java +++ b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/pojo/po/CsPqdifPathPO.java @@ -25,11 +25,8 @@ public class CsPqdifPathPO implements Serializable { @TableField("Name") private String name; - @TableField("Pqdif") - private byte[] pqdifContent; - - @TableField("Native_Version") - private String nativeVersion; + @TableField("File_Path") + private String filePath; @TableField("Record_Count") private Long recordCount; @@ -46,9 +43,6 @@ public class CsPqdifPathPO implements Serializable { @TableField(value = "Msg", typeHandler = JsonNodeTypeHandler.class) private JsonNode msg; - @TableField("Json_Str") - private String jsonStr; - @TableField("State") private Integer state; diff --git a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/pojo/vo/CsPqdifPathDetailVO.java b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/pojo/vo/CsPqdifPathDetailVO.java index 0b10ed9..d270d37 100644 --- a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/pojo/vo/CsPqdifPathDetailVO.java +++ b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/pojo/vo/CsPqdifPathDetailVO.java @@ -1,6 +1,5 @@ package com.njcn.gather.tool.parsepqdif.pojo.vo; -import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -18,9 +17,6 @@ public class CsPqdifPathDetailVO { @ApiModelProperty("PQDIF名称") private String name; - @ApiModelProperty("完整PQDIF解析结果JSON") - private String jsonStr; - - @JsonIgnore - private byte[] pqdifContent; + @ApiModelProperty("PQDIF原始文件存储路径") + private String filePath; } diff --git a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/pojo/vo/CsPqdifPathVO.java b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/pojo/vo/CsPqdifPathVO.java index eb722c3..b20929b 100644 --- a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/pojo/vo/CsPqdifPathVO.java +++ b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/pojo/vo/CsPqdifPathVO.java @@ -20,8 +20,8 @@ public class CsPqdifPathVO { @ApiModelProperty("PQDIF名称") private String name; - @ApiModelProperty("native解析库版本") - private String nativeVersion; + @ApiModelProperty("PQDIF原始文件存储路径") + private String filePath; @ApiModelProperty("Record总数") private Long recordCount; diff --git a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/pojo/vo/PqdifRecognizeResultVO.java b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/pojo/vo/PqdifRecognizeResultVO.java new file mode 100644 index 0000000..61c1702 --- /dev/null +++ b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/pojo/vo/PqdifRecognizeResultVO.java @@ -0,0 +1,81 @@ +package com.njcn.gather.tool.parsepqdif.pojo.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +@Data +@ApiModel("PQDIF二次解析结果") +public class PqdifRecognizeResultVO { + + @ApiModelProperty("状态:SUCCESS / FAILED") + private String status; + + @ApiModelProperty("提示信息") + private String message; + + @ApiModelProperty("Observation数量") + private Long observationCount; + + @ApiModelProperty("Channel数量") + private Long channelCount; + + @ApiModelProperty("Series数量") + private Long seriesCount; + + @ApiModelProperty("可识别Observation列表") + private List observations; + + @ApiModelProperty("未识别字段列表") + private List unknownItems; + + @Data + public static class RecognizedObservationVO { + private Long recordIndex; + private String name; + private String startTime; + private List channels; + } + + @Data + public static class RecognizedChannelVO { + private Long channelIndex; + private String name; + private String phaseCode; + private String phaseName; + private String measurementName; + private Long phaseId; + private Long quantityMeasuredId; + private String quantityTypeGuid; + private List series; + } + + @Data + public static class RecognizedSeriesVO { + private Long seriesIndex; + private String unitName; + private String characteristicName; + private String valueTypeName; + private String dataTypeName; + private Long quantityUnitsId; + private String quantityCharacteristicGuid; + private String valueTypeGuid; + private Long seriesBaseType; + private Double scale; + private Double offset; + private String dataStatus; + private String dataMessage; + private Integer valueCount; + private List firstValues; + } + + @Data + public static class UnknownItemVO { + private String path; + private String fieldName; + private String fieldValue; + private String message; + } +} diff --git a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/service/ParsePqdifService.java b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/service/ParsePqdifService.java index c74bc3a..7da2e68 100644 --- a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/service/ParsePqdifService.java +++ b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/service/ParsePqdifService.java @@ -1,6 +1,7 @@ package com.njcn.gather.tool.parsepqdif.service; import com.njcn.gather.tool.parsepqdif.pojo.vo.PqdifParseResponse; +import com.njcn.gather.tool.parsepqdif.pojo.vo.PqdifRecognizeResultVO; import org.springframework.web.multipart.MultipartFile; /** @@ -9,4 +10,6 @@ import org.springframework.web.multipart.MultipartFile; public interface ParsePqdifService { PqdifParseResponse parse(MultipartFile pqdifFile); + + PqdifRecognizeResultVO parseRecognizable(PqdifParseResponse parseResponse); } diff --git a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/service/impl/CsPqdifPathServiceImpl.java b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/service/impl/CsPqdifPathServiceImpl.java index d408388..5770fa1 100644 --- a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/service/impl/CsPqdifPathServiceImpl.java +++ b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/service/impl/CsPqdifPathServiceImpl.java @@ -1,7 +1,9 @@ package com.njcn.gather.tool.parsepqdif.service.impl; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.njcn.gather.tool.parsepqdif.mapper.CsPqdifPathMapper; import com.njcn.gather.tool.parsepqdif.pojo.param.CsPqdifPathParam; import com.njcn.gather.tool.parsepqdif.pojo.param.PqdifParseResultSaveParam; @@ -29,6 +31,8 @@ public class CsPqdifPathServiceImpl implements CsPqdifPathService { private static final int STATE_DELETED = 0; + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private final CsPqdifPathMapper csPqdifPathMapper; @Override @@ -71,7 +75,7 @@ public class CsPqdifPathServiceImpl implements CsPqdifPathService { @Transactional public boolean updatePqdifPath(CsPqdifPathParam.UpdateParam param) { CsPqdifPathParam.UpdateParam checkedParam = requireUpdateParam(param); - requirePqdifPath(checkedParam.getId()); + requireNormalPqdifPath(checkedParam.getId()); CsPqdifPathPO pqdifPath = buildPqdifPath(checkedParam); pqdifPath.setId(checkedParam.getId()); pqdifPath.setUpdateBy(currentUserId()); @@ -100,14 +104,15 @@ public class CsPqdifPathServiceImpl implements CsPqdifPathService { if (param == null) { throw new IllegalArgumentException("PQDIF解析结果不能为空"); } - CsPqdifPathPO pqdifPath = requirePqdifPath(pqdifId); - pqdifPath.setNativeVersion(trimToNull(param.getNativeVersion())); + String id = requireNormalPqdifPath(pqdifId); + CsPqdifPathPO pqdifPath = new CsPqdifPathPO(); + pqdifPath.setId(id); pqdifPath.setRecordCount(param.getRecordCount()); pqdifPath.setObservationCount(param.getObservationCount()); pqdifPath.setSampleValueCount(param.getSampleValueCount()); - pqdifPath.setResult(normalizeResult(param.getResult())); - pqdifPath.setMsg(param.getMsg()); - pqdifPath.setJsonStr(trimToNull(param.getJsonStr())); + Integer result = normalizeResult(param.getResult()); + pqdifPath.setResult(result); + pqdifPath.setMsg(buildStorageResultMsg(result)); pqdifPath.setUpdateBy(currentUserId()); pqdifPath.setUpdateTime(LocalDateTime.now()); return csPqdifPathMapper.updateById(pqdifPath) > 0; @@ -116,7 +121,7 @@ public class CsPqdifPathServiceImpl implements CsPqdifPathService { private CsPqdifPathPO buildPqdifPath(CsPqdifPathParam param) { CsPqdifPathPO pqdifPath = new CsPqdifPathPO(); pqdifPath.setName(requireText(param.getName(), "PQDIF名称不能为空")); - pqdifPath.setPqdifContent(param.getPqdifContent()); + pqdifPath.setFilePath(trimToNull(param.getFilePath())); return pqdifPath; } @@ -135,13 +140,13 @@ public class CsPqdifPathServiceImpl implements CsPqdifPathService { return param; } - private CsPqdifPathPO requirePqdifPath(String pqdifId) { + private String requireNormalPqdifPath(String pqdifId) { String id = requireText(pqdifId, "PQDIF记录ID不能为空"); - CsPqdifPathPO pqdifPath = csPqdifPathMapper.selectById(id); - if (pqdifPath == null || !Integer.valueOf(STATE_NORMAL).equals(pqdifPath.getState())) { + CsPqdifPathDetailVO pqdifPath = csPqdifPathMapper.selectPqdifPathDetailById(id); + if (pqdifPath == null) { throw new IllegalArgumentException("PQDIF记录不存在或已删除"); } - return pqdifPath; + return id; } private Integer normalizeResult(Integer result) { @@ -154,6 +159,18 @@ public class CsPqdifPathServiceImpl implements CsPqdifPathService { return result; } + private JsonNode buildStorageResultMsg(Integer result) { + ObjectNode msg = OBJECT_MAPPER.createObjectNode(); + if (Integer.valueOf(1).equals(result)) { + msg.put("storageResult", "SUCCESS"); + msg.put("message", "存储成功"); + } else { + msg.put("storageResult", "FAILED"); + msg.put("message", "存储失败"); + } + return msg; + } + private String requireText(String value, String message) { String text = trimToNull(value); if (text == null) { diff --git a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/service/impl/ParsePqdifServiceImpl.java b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/service/impl/ParsePqdifServiceImpl.java index 02cfa9f..9c6e2e3 100644 --- a/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/service/impl/ParsePqdifServiceImpl.java +++ b/tools/parse-pqdif/src/main/java/com/njcn/gather/tool/parsepqdif/service/impl/ParsePqdifServiceImpl.java @@ -1,6 +1,8 @@ package com.njcn.gather.tool.parsepqdif.service.impl; +import com.njcn.gather.tool.parsepqdif.component.PqdifSecondParseComponent; import com.njcn.gather.tool.parsepqdif.pojo.vo.PqdifParseResponse; +import com.njcn.gather.tool.parsepqdif.pojo.vo.PqdifRecognizeResultVO; import com.njcn.gather.tool.parsepqdif.reader.PqdifNativeReader; import com.njcn.gather.tool.parsepqdif.service.ParsePqdifService; import lombok.RequiredArgsConstructor; @@ -31,6 +33,7 @@ public class ParsePqdifServiceImpl implements ParsePqdifService { private static final String UNKNOWN_FAILED_REASON = "请检查文件内容或原生解析库状态"; private final PqdifNativeReader pqdifNativeReader; + private final PqdifSecondParseComponent pqdifSecondParseComponent; @Override public PqdifParseResponse parse(MultipartFile pqdifFile) { @@ -56,6 +59,11 @@ public class ParsePqdifServiceImpl implements ParsePqdifService { } } + @Override + public PqdifRecognizeResultVO parseRecognizable(PqdifParseResponse parseResponse) { + return pqdifSecondParseComponent.parseRecognizableData(parseResponse); + } + private Path createTempPqdifFile(MultipartFile pqdifFile, String suffix) throws Exception { // 原生解析库只接收文件路径,因此上传内容需先落到系统临时目录。 Path uploadDir = Paths.get(System.getProperty("java.io.tmpdir"), TEMP_DIR_NAME); diff --git a/tools/parse-pqdif/src/test/java/com/njcn/gather/tool/parsepqdif/PqdifDesktopFileTestProgram.java b/tools/parse-pqdif/src/test/java/com/njcn/gather/tool/parsepqdif/PqdifDesktopFileTestProgram.java new file mode 100644 index 0000000..061d120 --- /dev/null +++ b/tools/parse-pqdif/src/test/java/com/njcn/gather/tool/parsepqdif/PqdifDesktopFileTestProgram.java @@ -0,0 +1,151 @@ +package com.njcn.gather.tool.parsepqdif; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.njcn.gather.tool.parsepqdif.component.PqdifSecondParseComponent; +import com.njcn.gather.tool.parsepqdif.pojo.vo.PqdifParseResponse; +import com.njcn.gather.tool.parsepqdif.pojo.vo.PqdifRecognizeResultVO; +import com.njcn.gather.tool.parsepqdif.reader.PqdifNativeReader; +import com.njcn.gather.tool.parsepqdif.service.ParsePqdifService; +import com.njcn.gather.tool.parsepqdif.service.impl.ParsePqdifServiceImpl; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.Arrays; +import java.util.List; + +public class PqdifDesktopFileTestProgram { + + private static final String PQDIF_FILE_NAME = "PQMonitor_PQM1_20210512_1000_01.pqd"; + private static final String MULTIPART_PARAM_NAME = "pqdifFile"; + private static final String SAMPLE_RESOURCE_PATH = "pqdif-samples/" + PQDIF_FILE_NAME; + + public static void main(String[] args) throws Exception { + Path pqdifPath = resolvePqdifPath(args); + if (!Files.isRegularFile(pqdifPath)) { + System.out.println("PQDIF file not found: " + pqdifPath.toAbsolutePath()); + System.out.println("Usage: java " + PqdifDesktopFileTestProgram.class.getName() + " "); + return; + } + + System.out.println("Parsing PQDIF file: " + pqdifPath.toAbsolutePath()); + + ParsePqdifService parsePqdifService = new ParsePqdifServiceImpl(new PqdifNativeReader(), new PqdifSecondParseComponent()); + PqdifParseResponse response = parsePqdifService.parse(new PathMultipartFile(MULTIPART_PARAM_NAME, pqdifPath)); + System.out.println("Original parse response:"); + printResponse(response); + + PqdifRecognizeResultVO recognizableResult = parsePqdifService.parseRecognizable(response); + System.out.println("Recognizable parse response:"); + printResponse(recognizableResult); + } + + private static Path resolvePqdifPath(String[] args) { + if (args != null && args.length > 0 && args[0] != null && !args[0].trim().isEmpty()) { + return Paths.get(args[0].trim()).toAbsolutePath().normalize(); + } + + Path bundledSample = resolveBundledSamplePath(); + if (bundledSample != null && Files.isRegularFile(bundledSample)) { + return bundledSample; + } + + return defaultCandidatePaths().stream() + .filter(Files::isRegularFile) + .findFirst() + .orElse(defaultDesktopPath()); + } + + private static Path resolveBundledSamplePath() { + URL resource = PqdifDesktopFileTestProgram.class.getClassLoader().getResource(SAMPLE_RESOURCE_PATH); + if (resource == null || !"file".equalsIgnoreCase(resource.getProtocol())) { + return null; + } + try { + return Paths.get(resource.toURI()).toAbsolutePath().normalize(); + } catch (Exception e) { + return null; + } + } + + private static List defaultCandidatePaths() { + return Arrays.asList( + Paths.get("tools", "parse-pqdif", "src", "main", "resources", "pqdif-samples", PQDIF_FILE_NAME), + Paths.get("src", "main", "resources", "pqdif-samples", PQDIF_FILE_NAME), + defaultDesktopPath()); + } + + private static Path defaultDesktopPath() { + return Paths.get(System.getProperty("user.home"), "Desktop", PQDIF_FILE_NAME); + } + + private static void printResponse(Object response) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(); + System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(response)); + } + + private static class PathMultipartFile implements MultipartFile { + + private final String name; + private final Path path; + + private PathMultipartFile(String name, Path path) { + this.name = name; + this.path = path; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getOriginalFilename() { + return path.getFileName().toString(); + } + + @Override + public String getContentType() { + return "application/octet-stream"; + } + + @Override + public boolean isEmpty() { + try { + return Files.size(path) == 0; + } catch (IOException e) { + return true; + } + } + + @Override + public long getSize() { + try { + return Files.size(path); + } catch (IOException e) { + return 0L; + } + } + + @Override + public byte[] getBytes() throws IOException { + return Files.readAllBytes(path); + } + + @Override + public InputStream getInputStream() throws IOException { + return Files.newInputStream(path); + } + + @Override + public void transferTo(File dest) throws IOException { + Files.copy(path, dest.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + } +} diff --git a/tools/parse-pqdif/src/test/java/com/njcn/gather/tool/parsepqdif/component/PqdifSecondParseComponentTest.java b/tools/parse-pqdif/src/test/java/com/njcn/gather/tool/parsepqdif/component/PqdifSecondParseComponentTest.java new file mode 100644 index 0000000..6bdca5c --- /dev/null +++ b/tools/parse-pqdif/src/test/java/com/njcn/gather/tool/parsepqdif/component/PqdifSecondParseComponentTest.java @@ -0,0 +1,28 @@ +package com.njcn.gather.tool.parsepqdif.component; + +import com.njcn.gather.tool.parsepqdif.pojo.vo.PqdifParseResponse; +import com.njcn.gather.tool.parsepqdif.pojo.vo.PqdifRecognizeResultVO; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class PqdifSecondParseComponentTest { + + @Test + void parseRecognizableDataFormatsStartTimeAsStandard24HourText() { + PqdifParseResponse parseResponse = new PqdifParseResponse(); + parseResponse.setStatus("SUCCESS"); + parseResponse.setObservations(new ArrayList()); + + PqdifParseResponse.ObservationVO observation = new PqdifParseResponse.ObservationVO(); + observation.setTimeStartText("2026-06-22T15:04:05.123"); + observation.setChannels(new ArrayList()); + parseResponse.getObservations().add(observation); + + PqdifRecognizeResultVO result = new PqdifSecondParseComponent().parseRecognizableData(parseResponse); + + assertEquals("2026-06-22 15:04:05", result.getObservations().get(0).getStartTime()); + } +}