From 03df9d3a1013490364d81344d0ddf93d0a1902b7 Mon Sep 17 00:00:00 2001
From: yexb <553699424@qq.com>
Date: Thu, 23 Apr 2026 11:06:04 +0800
Subject: [PATCH] =?UTF-8?q?=E6=9C=80=E8=BF=91=E6=96=B0=E5=A2=9E=E7=9A=84?=
=?UTF-8?q?=E5=B7=A5=E4=BD=9C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 9 +-
entrance/pom.xml | 5 +
pom.xml | 1 +
systemmonitor/README.md | 29 +
systemmonitor/disk-monitor/README.md | 11 +
systemmonitor/disk-monitor/pom.xml | 37 ++
.../DiskMonitorNotificationComponent.java | 250 +++++++++
.../disk/config/DiskMonitorModuleConfig.java | 10 +
.../disk/constant/DiskMonitorConstant.java | 43 ++
.../constant/DiskMonitorValidMessage.java | 19 +
.../controller/DiskMonitorJobController.java | 72 +++
.../DiskMonitorNotifyController.java | 49 ++
.../DiskMonitorPolicyController.java | 60 ++
.../disk/enums/DiskMonitorResponseEnum.java | 37 ++
.../event/DiskMonitorPolicyChangedEvent.java | 7 +
.../disk/mapper/DiskMonitorJobMapper.java | 10 +
.../mapper/DiskMonitorNotifyLogMapper.java | 10 +
.../disk/mapper/DiskMonitorPolicyMapper.java | 10 +
.../disk/mapper/DiskMonitorResultMapper.java | 10 +
.../disk/mapper/DiskMonitorTargetMapper.java | 10 +
.../mapper/mapping/DiskMonitorJobMapper.xml | 5 +
.../mapping/DiskMonitorNotifyLogMapper.xml | 5 +
.../mapping/DiskMonitorPolicyMapper.xml | 5 +
.../mapping/DiskMonitorResultMapper.xml | 5 +
.../mapping/DiskMonitorTargetMapper.xml | 5 +
.../pojo/dto/DiskMonitorNotifyHttpItem.java | 31 ++
.../pojo/dto/DiskMonitorNotifyPathItem.java | 23 +
.../disk/pojo/param/DiskMonitorParam.java | 131 +++++
.../disk/pojo/po/DiskMonitorJob.java | 58 ++
.../disk/pojo/po/DiskMonitorNotifyLog.java | 58 ++
.../disk/pojo/po/DiskMonitorPolicy.java | 59 ++
.../disk/pojo/po/DiskMonitorResult.java | 65 +++
.../disk/pojo/po/DiskMonitorTarget.java | 74 +++
.../disk/pojo/vo/DiskMonitorVO.java | 124 +++++
.../schedule/DiskMonitorScheduleManager.java | 94 ++++
.../disk/service/IDiskMonitorJobService.java | 27 +
.../service/IDiskMonitorNotifyService.java | 11 +
.../service/IDiskMonitorPolicyService.java | 26 +
.../impl/DiskMonitorJobServiceImpl.java | 517 ++++++++++++++++++
.../impl/DiskMonitorNotifyServiceImpl.java | 34 ++
.../impl/DiskMonitorPolicyServiceImpl.java | 341 ++++++++++++
.../sql/disk-monitor/disk-monitor-init.sql | 104 ++++
systemmonitor/pom.xml | 22 +
tools/mms-mapping/API-getIcdMmsJson.md | 371 +++++++++++++
tools/mms-mapping/README.md | 221 ++++++++
.../application/MappingTaskAppService.java | 135 -----
.../command/GenerateFromIcdCommand.java | 102 ----
.../command/IndexBindingCommand.java | 51 --
.../command/IndexSelectionGroupCommand.java | 43 --
.../result/GenerateMappingResult.java | 42 --
.../DefaultTemplateLoader.java | 15 +-
.../FileStorageService.java | 8 +-
.../IcdParserService.java | 8 +-
.../IndexAnalysisService.java | 51 +-
.../IndexValidationService.java | 30 +-
.../MappingDocumentSerializer.java | 13 +-
.../MappingGenerationService.java | 103 +++-
.../component/MappingRequestConverter.java | 132 +++++
.../component/MappingResponseConverter.java | 85 +++
.../mapping/config/MappingModuleConfig.java | 37 +-
.../mapping/controller/MappingController.java | 94 +++-
.../GenerateMappingFromIcdRequest.java | 89 ---
.../request/IndexBindingRequest.java | 59 --
.../request/IndexSelectionGroupRequest.java | 53 --
.../IndexCandidateReportItemResponse.java | 54 --
.../response/IndexCandidateResponse.java | 71 ---
.../response/MappingDocumentResponse.java | 26 -
.../response/MappingTaskResponse.java | 40 --
.../converter/MappingRequestConverter.java | 82 ---
.../converter/MappingResponseConverter.java | 70 ---
.../domain/model/analysis/IndexCandidate.java | 113 ----
.../analysis/IndexCandidateReportItem.java | 56 --
.../model/analysis/ValidationResult.java | 18 -
.../mapping/domain/model/icd/DataSetNode.java | 17 -
.../domain/model/icd/DoiElementNode.java | 31 --
.../icd/mapping/domain/model/icd/DoiNode.java | 30 -
.../mapping/domain/model/icd/FcdaNode.java | 36 --
.../mapping/domain/model/icd/IcdDocument.java | 46 --
.../icd/mapping/domain/model/icd/IedNode.java | 20 -
.../icd/mapping/domain/model/icd/LnNode.java | 32 --
.../domain/model/icd/LogicalDeviceNode.java | 14 -
.../domain/model/icd/ReportControlNode.java | 29 -
.../intermediate/DataSetSelectionState.java | 103 ----
.../intermediate/ReportAndDataSetState.java | 59 --
.../intermediate/ReportBindingState.java | 73 ---
.../model/intermediate/ReportGroupState.java | 100 ----
.../model/mapping/DataSetGroupItem.java | 43 --
.../mapping/domain/model/mapping/DoiItem.java | 120 ----
.../domain/model/mapping/InstItem.java | 43 --
.../domain/model/mapping/MappingDocument.java | 127 -----
.../domain/model/mapping/ReportMapItem.java | 124 -----
.../mapping/domain/model/mapping/SdiItem.java | 43 --
.../domain/model/mapping/TypeItem.java | 29 -
.../model/template/DefaultTemplate.java | 345 ------------
.../parser/SclGeneratedModelReader.java | 116 ++--
.../parser/SclParserAdapter.java | 15 +-
.../parser/SclTraversalSupport.java | 19 +-
.../pojo/bo/GenerateMappingResult.java | 48 ++
.../bo}/analysis/IndexAnalysis.java | 12 +-
.../pojo/bo/analysis/IndexCandidate.java | 51 ++
.../bo/analysis/IndexCandidateReportItem.java | 26 +
.../pojo/bo/analysis/ValidationResult.java | 18 +
.../icd/mapping/pojo/bo/icd/DataSetNode.java | 18 +
.../mapping/pojo/bo/icd/DoiElementNode.java | 31 ++
.../icd/mapping/pojo/bo/icd/DoiNode.java | 30 +
.../icd/mapping/pojo/bo/icd/FcdaNode.java | 36 ++
.../icd/mapping/pojo/bo/icd/IcdDocument.java | 46 ++
.../icd/mapping/pojo/bo/icd/IedNode.java | 21 +
.../icd/mapping/pojo/bo/icd/LnNode.java | 32 ++
.../pojo/bo/icd/LogicalDeviceNode.java | 15 +
.../pojo/bo/icd/ReportControlNode.java | 33 ++
.../pojo/bo/mapping/DataSetGroupItem.java | 21 +
.../icd/mapping/pojo/bo/mapping/DoiItem.java | 42 ++
.../icd/mapping/pojo/bo/mapping/InstItem.java | 21 +
.../pojo/bo/mapping/MappingDocument.java | 63 +++
.../pojo/bo/mapping/ReportMapItem.java | 60 ++
.../icd/mapping/pojo/bo/mapping/SdiItem.java | 21 +
.../icd/mapping/pojo/bo/mapping/TypeItem.java | 15 +
.../pojo/bo/state/DataSetSelectionState.java | 49 ++
.../pojo/bo/state/ReportAndDataSetState.java | 29 +
.../pojo/bo/state/ReportBindingState.java | 27 +
.../pojo/bo/state/ReportGroupState.java | 38 ++
.../pojo/bo/template/DefaultTemplate.java | 255 +++++++++
.../pojo/dto/GenerateFromIcdCommand.java | 43 ++
.../mapping/pojo/dto/IndexBindingCommand.java | 21 +
.../pojo/dto/IndexSelectionGroupCommand.java | 21 +
.../{ => pojo}/enums/GenerateStatus.java | 6 +-
.../param/GenerateMappingFromIcdRequest.java | 38 ++
.../pojo/param/IndexBindingRequest.java | 24 +
.../param/IndexSelectionGroupRequest.java | 28 +
.../param/SubmitIndexSelectionRequest.java | 37 ++
.../vo/IndexCandidateReportItemResponse.java | 27 +
.../pojo/vo/IndexCandidateResponse.java | 33 ++
.../pojo/vo/MappingDocumentResponse.java | 28 +
.../mapping/pojo/vo/MappingTaskResponse.java | 39 ++
.../mapping/service/MappingTaskService.java | 36 ++
.../service/impl/MappingTaskServiceImpl.java | 271 +++++++++
.../gather/icd/mapping/utils/DateUtils.java | 24 +-
.../gather/icd/mapping/utils/JsonUtils.java | 11 +-
.../debug/GetIcdMmsJsonDebugRunner.java | 214 ++++++++
140 files changed, 5591 insertions(+), 2767 deletions(-)
create mode 100644 systemmonitor/README.md
create mode 100644 systemmonitor/disk-monitor/README.md
create mode 100644 systemmonitor/disk-monitor/pom.xml
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/component/DiskMonitorNotificationComponent.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/config/DiskMonitorModuleConfig.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/constant/DiskMonitorConstant.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/constant/DiskMonitorValidMessage.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/controller/DiskMonitorJobController.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/controller/DiskMonitorNotifyController.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/controller/DiskMonitorPolicyController.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/enums/DiskMonitorResponseEnum.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/event/DiskMonitorPolicyChangedEvent.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/DiskMonitorJobMapper.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/DiskMonitorNotifyLogMapper.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/DiskMonitorPolicyMapper.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/DiskMonitorResultMapper.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/DiskMonitorTargetMapper.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/mapping/DiskMonitorJobMapper.xml
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/mapping/DiskMonitorNotifyLogMapper.xml
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/mapping/DiskMonitorPolicyMapper.xml
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/mapping/DiskMonitorResultMapper.xml
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/mapping/DiskMonitorTargetMapper.xml
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/dto/DiskMonitorNotifyHttpItem.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/dto/DiskMonitorNotifyPathItem.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/param/DiskMonitorParam.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/po/DiskMonitorJob.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/po/DiskMonitorNotifyLog.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/po/DiskMonitorPolicy.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/po/DiskMonitorResult.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/po/DiskMonitorTarget.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/vo/DiskMonitorVO.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/schedule/DiskMonitorScheduleManager.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/IDiskMonitorJobService.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/IDiskMonitorNotifyService.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/IDiskMonitorPolicyService.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/impl/DiskMonitorJobServiceImpl.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/impl/DiskMonitorNotifyServiceImpl.java
create mode 100644 systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/impl/DiskMonitorPolicyServiceImpl.java
create mode 100644 systemmonitor/disk-monitor/src/main/resources/sql/disk-monitor/disk-monitor-init.sql
create mode 100644 systemmonitor/pom.xml
create mode 100644 tools/mms-mapping/API-getIcdMmsJson.md
create mode 100644 tools/mms-mapping/README.md
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/application/MappingTaskAppService.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/application/command/GenerateFromIcdCommand.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/application/command/IndexBindingCommand.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/application/command/IndexSelectionGroupCommand.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/application/result/GenerateMappingResult.java
rename tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/{domain/service => component}/DefaultTemplateLoader.java (85%)
rename tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/{infrastructure/storage => component}/FileStorageService.java (80%)
rename tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/{domain/service => component}/IcdParserService.java (67%)
rename tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/{domain/service => component}/IndexAnalysisService.java (78%)
rename tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/{domain/service => component}/IndexValidationService.java (81%)
rename tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/{infrastructure/serializer => component}/MappingDocumentSerializer.java (78%)
rename tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/{domain/service => component}/MappingGenerationService.java (91%)
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/MappingRequestConverter.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/MappingResponseConverter.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/request/GenerateMappingFromIcdRequest.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/request/IndexBindingRequest.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/request/IndexSelectionGroupRequest.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/response/IndexCandidateReportItemResponse.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/response/IndexCandidateResponse.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/response/MappingDocumentResponse.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/response/MappingTaskResponse.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/converter/MappingRequestConverter.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/converter/MappingResponseConverter.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/analysis/IndexCandidate.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/analysis/IndexCandidateReportItem.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/analysis/ValidationResult.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/DataSetNode.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/DoiElementNode.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/DoiNode.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/FcdaNode.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/IcdDocument.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/IedNode.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/LnNode.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/LogicalDeviceNode.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/ReportControlNode.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/intermediate/DataSetSelectionState.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/intermediate/ReportAndDataSetState.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/intermediate/ReportBindingState.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/intermediate/ReportGroupState.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/DataSetGroupItem.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/DoiItem.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/InstItem.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/MappingDocument.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/ReportMapItem.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/SdiItem.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/TypeItem.java
delete mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/template/DefaultTemplate.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/GenerateMappingResult.java
rename tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/{domain/model => pojo/bo}/analysis/IndexAnalysis.java (50%)
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/analysis/IndexCandidate.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/analysis/IndexCandidateReportItem.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/analysis/ValidationResult.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/icd/DataSetNode.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/icd/DoiElementNode.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/icd/DoiNode.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/icd/FcdaNode.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/icd/IcdDocument.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/icd/IedNode.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/icd/LnNode.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/icd/LogicalDeviceNode.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/icd/ReportControlNode.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/mapping/DataSetGroupItem.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/mapping/DoiItem.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/mapping/InstItem.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/mapping/MappingDocument.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/mapping/ReportMapItem.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/mapping/SdiItem.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/mapping/TypeItem.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/state/DataSetSelectionState.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/state/ReportAndDataSetState.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/state/ReportBindingState.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/state/ReportGroupState.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/bo/template/DefaultTemplate.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/dto/GenerateFromIcdCommand.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/dto/IndexBindingCommand.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/dto/IndexSelectionGroupCommand.java
rename tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/{ => pojo}/enums/GenerateStatus.java (52%)
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/GenerateMappingFromIcdRequest.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/IndexBindingRequest.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/IndexSelectionGroupRequest.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/SubmitIndexSelectionRequest.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IndexCandidateReportItemResponse.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IndexCandidateResponse.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/MappingDocumentResponse.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/MappingTaskResponse.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/service/MappingTaskService.java
create mode 100644 tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/service/impl/MappingTaskServiceImpl.java
create mode 100644 tools/mms-mapping/src/test/java/com/njcn/gather/icd/mapping/debug/GetIcdMmsJsonDebugRunner.java
diff --git a/README.md b/README.md
index 78d6454..08a7f53 100644
--- a/README.md
+++ b/README.md
@@ -14,10 +14,15 @@ CN_Tool 是一个基于 Spring Boot 的多模块后端聚合工程,当前仓
- `entrance`
- `system`
+- `systemmonitor`
- `user`
- `detection`
- `tools`
+其中 `systemmonitor` 当前包含:
+
+- `disk-monitor`
+
其中 `tools` 当前包含:
- `activate-tool`
@@ -30,7 +35,7 @@ CN_Tool 是一个基于 Spring Boot 的多模块后端聚合工程,当前仓
- `entrance/src/main/java/com/njcn/gather/EntranceApplication.java`
-`entrance` 模块聚合了 `system`、`user`、`detection`、`activate-tool`、`wave-tool`、`mms-mapping`,是当前运行时主入口。
+`entrance` 模块聚合了 `system`、`disk-monitor`、`user`、`detection`、`activate-tool`、`wave-tool`、`mms-mapping`,是当前运行时主入口。
## 技术基线
@@ -71,6 +76,8 @@ P0 已补齐基线文档,建议按以下顺序阅读:
- 负责认证、用户、角色、菜单资源相关能力
- `system`
- 负责字典、日志、系统配置、注册资源相关能力
+- `systemmonitor/disk-monitor`
+ - 负责磁盘监控相关能力的独立扩展实现
- `detection`
- 当前以通信基础设施为主,包含 WebSocket / Netty 相关组件
- `tools/activate-tool`
diff --git a/entrance/pom.xml b/entrance/pom.xml
index c5cdff1..24d6a1d 100644
--- a/entrance/pom.xml
+++ b/entrance/pom.xml
@@ -16,6 +16,11 @@
system
1.0.0
+
+ com.njcn.gather
+ disk-monitor
+ 1.0.0
+
com.njcn.gather
detection
diff --git a/pom.xml b/pom.xml
index ce81522..0953afe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,6 +12,7 @@
entrance
system
+ systemmonitor
user
detection
tools
diff --git a/systemmonitor/README.md b/systemmonitor/README.md
new file mode 100644
index 0000000..36e09ac
--- /dev/null
+++ b/systemmonitor/README.md
@@ -0,0 +1,29 @@
+# System Monitor 模块说明
+
+## 当前状态
+
+`systemmonitor` 当前作为系统监控能力聚合模块使用。
+
+当前真实保留的子模块有:
+
+- `disk-monitor`
+
+## 当前结构
+
+```text
+systemmonitor/
+└── disk-monitor/
+```
+
+## disk-monitor 的职责
+
+`disk-monitor` 预留用于承载磁盘监控相关能力,后续可在该模块内继续补充:
+
+- 磁盘监控配置管理
+- 磁盘巡检任务
+- 磁盘容量计算与阈值判定
+- 磁盘预警与告警留痕
+
+## 模块定位
+
+当前将磁盘监控拆分到 `systemmonitor/disk-monitor`,目的是避免将系统监控扩展逻辑直接混入 `system` 公共基础模块,便于后续继续扩展 CPU、内存或进程级监控能力。
diff --git a/systemmonitor/disk-monitor/README.md b/systemmonitor/disk-monitor/README.md
new file mode 100644
index 0000000..ab9683c
--- /dev/null
+++ b/systemmonitor/disk-monitor/README.md
@@ -0,0 +1,11 @@
+# disk-monitor
+
+`disk-monitor` 模块用于承载磁盘监控相关代码。
+
+当前这一版只完成模块骨架接入,后续可以在该模块内继续补充:
+
+- Controller
+- Service
+- 定时巡检任务
+- 磁盘容量采集与阈值判断
+- 日志留痕与 SQL 脚本
diff --git a/systemmonitor/disk-monitor/pom.xml b/systemmonitor/disk-monitor/pom.xml
new file mode 100644
index 0000000..a1333d8
--- /dev/null
+++ b/systemmonitor/disk-monitor/pom.xml
@@ -0,0 +1,37 @@
+
+
+ 4.0.0
+
+
+ com.njcn.gather
+ systemmonitor
+ 1.0.0
+
+
+ disk-monitor
+ jar
+ disk-monitor
+
+
+
+ com.njcn
+ njcn-common
+ 0.0.1
+
+
+
+ com.njcn
+ mybatis-plus
+ 0.0.1
+
+
+
+ com.njcn
+ spingboot2.3.12
+ 2.3.12
+
+
+
+
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/component/DiskMonitorNotificationComponent.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/component/DiskMonitorNotificationComponent.java
new file mode 100644
index 0000000..b8d2ae7
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/component/DiskMonitorNotificationComponent.java
@@ -0,0 +1,250 @@
+package com.njcn.gather.systemmonitor.disk.component;
+
+import cn.hutool.core.util.StrUtil;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.njcn.gather.systemmonitor.disk.constant.DiskMonitorConstant;
+import com.njcn.gather.systemmonitor.disk.mapper.DiskMonitorNotifyLogMapper;
+import com.njcn.gather.systemmonitor.disk.pojo.dto.DiskMonitorNotifyHttpItem;
+import com.njcn.gather.systemmonitor.disk.pojo.dto.DiskMonitorNotifyPathItem;
+import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorJob;
+import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorNotifyLog;
+import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorResult;
+import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorTarget;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.io.OutputStream;
+import java.math.BigDecimal;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * 磁盘监控通知发送组件。
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class DiskMonitorNotificationComponent {
+
+ private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+ private static final TypeReference> PATH_LIST_TYPE = new TypeReference>() {
+ };
+ private static final TypeReference> HTTP_LIST_TYPE = new TypeReference>() {
+ };
+
+ private final DiskMonitorNotifyLogMapper notifyLogMapper;
+ private final ObjectMapper objectMapper;
+
+ public void sendNotifications(DiskMonitorJob job, DiskMonitorTarget target, DiskMonitorResult result,
+ BigDecimal usedPercent, String currentStatus, String notifyReason,
+ String notifyLevel, LocalDateTime scanTime, String message) {
+ List pathItems;
+ List httpItems;
+ try {
+ pathItems = parsePathItems(target.getNotifyPathListJson());
+ httpItems = parseHttpItems(target.getNotifyHttpListJson());
+ } catch (Exception exception) {
+ insertFailedNotifyLog(job, result, target, notifyLevel, "N/A", "通知配置解析失败: " + exception.getMessage());
+ return;
+ }
+
+ boolean pathConfigured = isEnabled(target.getNotifyPathEnabled()) && pathItems.stream().anyMatch(item -> Boolean.TRUE.equals(item.getEnabled()));
+ boolean httpConfigured = isEnabled(target.getNotifyHttpEnabled()) && httpItems.stream().anyMatch(item -> Boolean.TRUE.equals(item.getEnabled()));
+ if (!pathConfigured && !httpConfigured) {
+ insertFailedNotifyLog(job, result, target, notifyLevel, "N/A", "当前盘符未配置可用通知通道");
+ return;
+ }
+
+ String notifyTitle = buildNotifyTitle(target.getDriveLetter(), notifyLevel, usedPercent);
+ String notifyContent = buildNotifyContent(job, target, usedPercent, currentStatus, notifyLevel, scanTime, message);
+
+ if (pathConfigured) {
+ for (DiskMonitorNotifyPathItem item : pathItems) {
+ if (!Boolean.TRUE.equals(item.getEnabled())) {
+ continue;
+ }
+ sendPathNotification(job, target, result, notifyReason, notifyLevel, notifyTitle, notifyContent, item,
+ usedPercent, currentStatus, scanTime, message);
+ }
+ }
+ if (httpConfigured) {
+ for (DiskMonitorNotifyHttpItem item : httpItems) {
+ if (!Boolean.TRUE.equals(item.getEnabled())) {
+ continue;
+ }
+ sendHttpNotification(job, target, result, notifyReason, notifyLevel, notifyTitle, notifyContent, item,
+ usedPercent, currentStatus, scanTime, message);
+ }
+ }
+ }
+
+ private void sendPathNotification(DiskMonitorJob job, DiskMonitorTarget target, DiskMonitorResult result,
+ String notifyReason, String notifyLevel, String notifyTitle, String notifyContent,
+ DiskMonitorNotifyPathItem item, BigDecimal usedPercent, String currentStatus,
+ LocalDateTime scanTime, String message) {
+ DiskMonitorNotifyLog notifyLog = createNotifyLog(job, result, target, notifyLevel, notifyTitle, notifyContent,
+ DiskMonitorConstant.CHANNEL_TYPE_PATH, item.getPath());
+ try {
+ Path directoryPath = Paths.get(item.getPath());
+ Files.createDirectories(directoryPath);
+ String fileName = String.format("disk-monitor-%s-%s-%s.json",
+ job.getJobNo(), target.getDriveLetter().replace(":", ""),
+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")));
+ Path filePath = directoryPath.resolve(fileName);
+ Map payload = buildNotifyPayload(job, target, usedPercent, currentStatus, notifyReason, notifyLevel, scanTime, message);
+ Files.write(filePath, objectMapper.writerWithDefaultPrettyPrinter().writeValueAsBytes(payload));
+ notifyLog.setSendStatus(DiskMonitorConstant.SEND_STATUS_SUCCESS);
+ notifyLog.setResponseMessage("写入成功:" + filePath.toString());
+ } catch (Exception exception) {
+ log.error("磁盘监控路径通知发送失败,driveLetter={}, path={}", target.getDriveLetter(), item.getPath(), exception);
+ notifyLog.setSendStatus(DiskMonitorConstant.SEND_STATUS_FAILED);
+ notifyLog.setResponseMessage(exception.getMessage());
+ }
+ notifyLogMapper.insert(notifyLog);
+ }
+
+ private void sendHttpNotification(DiskMonitorJob job, DiskMonitorTarget target, DiskMonitorResult result,
+ String notifyReason, String notifyLevel, String notifyTitle, String notifyContent,
+ DiskMonitorNotifyHttpItem item, BigDecimal usedPercent, String currentStatus,
+ LocalDateTime scanTime, String message) {
+ DiskMonitorNotifyLog notifyLog = createNotifyLog(job, result, target, notifyLevel, notifyTitle, notifyContent,
+ DiskMonitorConstant.CHANNEL_TYPE_HTTP, item.getUrl());
+ HttpURLConnection connection = null;
+ try {
+ Map payload = buildNotifyPayload(job, target, usedPercent, currentStatus, notifyReason, notifyLevel, scanTime, message);
+ byte[] body = objectMapper.writeValueAsBytes(payload);
+ URL url = new URL(item.getUrl());
+ connection = (HttpURLConnection) url.openConnection();
+ connection.setRequestMethod(StrUtil.blankToDefault(item.getMethod(), DiskMonitorConstant.HTTP_METHOD_POST).trim().toUpperCase(Locale.ROOT));
+ connection.setConnectTimeout(resolveTimeout(item.getTimeoutMs()));
+ connection.setReadTimeout(resolveTimeout(item.getTimeoutMs()));
+ connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
+ connection.setDoInput(true);
+ if (!"GET".equalsIgnoreCase(connection.getRequestMethod())) {
+ connection.setDoOutput(true);
+ try (OutputStream outputStream = connection.getOutputStream()) {
+ outputStream.write(body);
+ }
+ }
+ int statusCode = connection.getResponseCode();
+ notifyLog.setSendStatus(statusCode >= 200 && statusCode < 300
+ ? DiskMonitorConstant.SEND_STATUS_SUCCESS
+ : DiskMonitorConstant.SEND_STATUS_FAILED);
+ notifyLog.setResponseMessage(statusCode + " " + connection.getResponseMessage());
+ } catch (Exception exception) {
+ log.error("磁盘监控HTTP通知发送失败,driveLetter={}, url={}", target.getDriveLetter(), item.getUrl(), exception);
+ notifyLog.setSendStatus(DiskMonitorConstant.SEND_STATUS_FAILED);
+ notifyLog.setResponseMessage(exception.getMessage());
+ } finally {
+ if (connection != null) {
+ connection.disconnect();
+ }
+ }
+ notifyLogMapper.insert(notifyLog);
+ }
+
+ private Map buildNotifyPayload(DiskMonitorJob job, DiskMonitorTarget target, BigDecimal usedPercent,
+ String currentStatus, String notifyReason, String notifyLevel,
+ LocalDateTime scanTime, String message) {
+ Map payload = new LinkedHashMap<>();
+ payload.put("jobNo", job.getJobNo());
+ payload.put("driveLetter", target.getDriveLetter());
+ payload.put("currentStatus", currentStatus);
+ payload.put("notifyReason", notifyReason);
+ payload.put("notifyLevel", notifyLevel);
+ payload.put("usedPercent", usedPercent);
+ payload.put("warningUsagePercent", target.getWarningUsagePercent());
+ payload.put("alarmUsagePercent", target.getAlarmUsagePercent());
+ payload.put("scanTime", formatDateTime(scanTime));
+ payload.put("message", message);
+ return payload;
+ }
+
+ private String buildNotifyTitle(String driveLetter, String notifyLevel, BigDecimal usedPercent) {
+ return String.format("磁盘监控通知[%s] %s 使用率%s%%",
+ notifyLevel, driveLetter, usedPercent == null ? "0.00" : usedPercent.toPlainString());
+ }
+
+ private String buildNotifyContent(DiskMonitorJob job, DiskMonitorTarget target, BigDecimal usedPercent,
+ String currentStatus, String notifyLevel, LocalDateTime scanTime, String message) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("任务编号:").append(job.getJobNo())
+ .append(";盘符:").append(target.getDriveLetter())
+ .append(";当前状态:").append(currentStatus)
+ .append(";通知级别:").append(notifyLevel)
+ .append(";使用率:").append(usedPercent == null ? "0.00" : usedPercent.toPlainString()).append("%")
+ .append(";预警阈值:").append(target.getWarningUsagePercent()).append("%")
+ .append(";告警阈值:").append(target.getAlarmUsagePercent()).append("%")
+ .append(";扫描时间:").append(formatDateTime(scanTime));
+ if (StrUtil.isNotBlank(message)) {
+ builder.append(";说明:").append(message);
+ }
+ return builder.toString();
+ }
+
+ private DiskMonitorNotifyLog createNotifyLog(DiskMonitorJob job, DiskMonitorResult result, DiskMonitorTarget target,
+ String notifyLevel, String notifyTitle, String notifyContent,
+ String channelType, String channelTarget) {
+ DiskMonitorNotifyLog notifyLog = new DiskMonitorNotifyLog();
+ notifyLog.setJobId(job.getId());
+ notifyLog.setResultId(result.getId());
+ notifyLog.setTargetId(target.getId());
+ notifyLog.setDriveLetter(target.getDriveLetter());
+ notifyLog.setNotifyLevel(notifyLevel);
+ notifyLog.setChannelType(channelType);
+ notifyLog.setChannelTarget(channelTarget);
+ notifyLog.setNotifyTitle(notifyTitle);
+ notifyLog.setNotifyContent(notifyContent);
+ notifyLog.setSentAt(LocalDateTime.now());
+ return notifyLog;
+ }
+
+ private void insertFailedNotifyLog(DiskMonitorJob job, DiskMonitorResult result, DiskMonitorTarget target,
+ String notifyLevel, String channelTarget, String responseMessage) {
+ DiskMonitorNotifyLog notifyLog = createNotifyLog(job, result, target, notifyLevel,
+ "磁盘监控通知失败", responseMessage, DiskMonitorConstant.CHANNEL_TYPE_HTTP, channelTarget);
+ notifyLog.setSendStatus(DiskMonitorConstant.SEND_STATUS_FAILED);
+ notifyLog.setResponseMessage(responseMessage);
+ notifyLogMapper.insert(notifyLog);
+ }
+
+ private List parsePathItems(String json) throws Exception {
+ return readJsonList(json, PATH_LIST_TYPE);
+ }
+
+ private List parseHttpItems(String json) throws Exception {
+ return readJsonList(json, HTTP_LIST_TYPE);
+ }
+
+ private List readJsonList(String json, TypeReference> typeReference) throws Exception {
+ if (StrUtil.isBlank(json)) {
+ return new ArrayList<>();
+ }
+ List result = objectMapper.readValue(json, typeReference);
+ return result == null ? new ArrayList<>() : result;
+ }
+
+ private boolean isEnabled(Integer value) {
+ return value != null && value == 1;
+ }
+
+ private int resolveTimeout(Integer timeoutMs) {
+ return timeoutMs == null || timeoutMs <= 0 ? DiskMonitorConstant.DEFAULT_HTTP_TIMEOUT_MS : timeoutMs;
+ }
+
+ private String formatDateTime(LocalDateTime dateTime) {
+ return dateTime == null ? null : dateTime.format(DATE_TIME_FORMATTER);
+ }
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/config/DiskMonitorModuleConfig.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/config/DiskMonitorModuleConfig.java
new file mode 100644
index 0000000..d2a8b38
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/config/DiskMonitorModuleConfig.java
@@ -0,0 +1,10 @@
+package com.njcn.gather.systemmonitor.disk.config;
+
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 磁盘监控模块基础配置入口,后续磁盘监控相关 Bean 统一放在该模块下扩展。
+ */
+@Configuration
+public class DiskMonitorModuleConfig {
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/constant/DiskMonitorConstant.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/constant/DiskMonitorConstant.java
new file mode 100644
index 0000000..02973a9
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/constant/DiskMonitorConstant.java
@@ -0,0 +1,43 @@
+package com.njcn.gather.systemmonitor.disk.constant;
+
+/**
+ * 磁盘监控模块常量。
+ */
+public interface DiskMonitorConstant {
+
+ String DEFAULT_POLICY_NAME = "默认磁盘监控策略";
+ String DEFAULT_DAILY_RUN_TIME = "08:30:00";
+ String WARNING_NOTIFY_MODE_STATUS_CHANGE = "STATUS_CHANGE";
+ String ALARM_NOTIFY_MODE_EVERY_TIME = "EVERY_TIME";
+
+ String STATUS_UNKNOWN = "UNKNOWN";
+ String STATUS_NORMAL = "NORMAL";
+ String STATUS_WARNING = "WARNING";
+ String STATUS_ALARM = "ALARM";
+
+ String JOB_SOURCE_APP_START = "APP_START";
+ String JOB_SOURCE_DAILY_SCHEDULE = "DAILY_SCHEDULE";
+ String JOB_SOURCE_MANUAL = "MANUAL";
+
+ String JOB_STATUS_RUNNING = "RUNNING";
+ String JOB_STATUS_SUCCESS = "SUCCESS";
+ String JOB_STATUS_PARTIAL_SUCCESS = "PARTIAL_SUCCESS";
+ String JOB_STATUS_FAILED = "FAILED";
+
+ String NOTIFY_REASON_ALARM_EVERY_TIME = "ALARM_EVERY_TIME";
+ String NOTIFY_REASON_STATUS_CHANGED = "STATUS_CHANGED";
+ String NOTIFY_REASON_NO_NOTIFY = "NO_NOTIFY";
+
+ String NOTIFY_LEVEL_WARNING = "WARNING";
+ String NOTIFY_LEVEL_ALARM = "ALARM";
+ String NOTIFY_LEVEL_RECOVER = "RECOVER";
+
+ String CHANNEL_TYPE_PATH = "PATH";
+ String CHANNEL_TYPE_HTTP = "HTTP";
+
+ String SEND_STATUS_SUCCESS = "SUCCESS";
+ String SEND_STATUS_FAILED = "FAILED";
+
+ String HTTP_METHOD_POST = "POST";
+ int DEFAULT_HTTP_TIMEOUT_MS = 5000;
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/constant/DiskMonitorValidMessage.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/constant/DiskMonitorValidMessage.java
new file mode 100644
index 0000000..cd792c7
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/constant/DiskMonitorValidMessage.java
@@ -0,0 +1,19 @@
+package com.njcn.gather.systemmonitor.disk.constant;
+
+/**
+ * 磁盘监控参数校验提示。
+ */
+public interface DiskMonitorValidMessage {
+
+ String POLICY_NOT_NULL = "policy不能为空,请检查policy参数";
+ String TARGETS_NOT_NULL = "targets不能为空,请检查targets参数";
+ String DRIVE_LETTER_NOT_BLANK = "盘符不能为空,请检查driveLetter参数";
+ String DAILY_RUN_TIME_NOT_BLANK = "每日统一执行时间不能为空,请检查dailyRunTime参数";
+ String DAILY_RUN_TIME_FORMAT_ERROR = "每日统一执行时间格式错误,请使用HH:mm:ss";
+ String WARNING_USAGE_PERCENT_NOT_NULL = "预警使用率不能为空,请检查warningUsagePercent参数";
+ String ALARM_USAGE_PERCENT_NOT_NULL = "告警使用率不能为空,请检查alarmUsagePercent参数";
+ String USAGE_PERCENT_FORMAT_ERROR = "阈值范围必须在1-100之间,请检查阈值参数";
+ String JOB_SOURCE_NOT_BLANK = "任务来源不能为空,请检查jobSource参数";
+ String JOB_ID_NOT_NULL = "jobId不能为空,请检查jobId参数";
+ String POLICY_NAME_NOT_BLANK = "策略名称不能为空,请检查policyName参数";
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/controller/DiskMonitorJobController.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/controller/DiskMonitorJobController.java
new file mode 100644
index 0000000..5c45ace
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/controller/DiskMonitorJobController.java
@@ -0,0 +1,72 @@
+package com.njcn.gather.systemmonitor.disk.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.njcn.common.pojo.annotation.OperateInfo;
+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.common.utils.LogUtil;
+import com.njcn.gather.systemmonitor.disk.pojo.param.DiskMonitorParam;
+import com.njcn.gather.systemmonitor.disk.pojo.vo.DiskMonitorVO;
+import com.njcn.gather.systemmonitor.disk.service.IDiskMonitorJobService;
+import com.njcn.web.controller.BaseController;
+import com.njcn.web.utils.HttpResultUtil;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+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.RestController;
+
+import javax.validation.Valid;
+
+/**
+ * 磁盘监控任务接口。
+ */
+@Validated
+@Slf4j
+@Api(tags = "磁盘监控任务")
+@RestController
+@RequestMapping("/disk-monitor")
+@RequiredArgsConstructor
+public class DiskMonitorJobController extends BaseController {
+
+ private final IDiskMonitorJobService diskMonitorJobService;
+
+ @OperateInfo(info = LogEnum.SYSTEM_COMMON, operateType = OperateType.UPDATE)
+ @PostMapping("/job/run")
+ @ApiOperation("手动执行磁盘监控")
+ @ApiImplicitParam(name = "param", value = "手动执行参数", required = true)
+ public HttpResult run(@RequestBody @Valid DiskMonitorParam.JobRunParam param) {
+ String methodDescribe = getMethodDescribe("run");
+ LogUtil.njcnDebug(log, "{},执行磁盘监控任务,source={}", methodDescribe, param.getJobSource());
+ return HttpResultUtil.assembleResult(CommonResponseEnum.SUCCESS.getCode(), diskMonitorJobService.runJob(param), "任务已启动");
+ }
+
+ @OperateInfo(info = LogEnum.SYSTEM_COMMON)
+ @PostMapping("/job/list")
+ @ApiOperation("分页查询磁盘监控任务列表")
+ @ApiImplicitParam(name = "param", value = "任务分页查询参数", required = true)
+ public HttpResult> list(@RequestBody @Valid DiskMonitorParam.JobListParam param) {
+ String methodDescribe = getMethodDescribe("list");
+ LogUtil.njcnDebug(log, "{},分页查询磁盘监控任务列表", methodDescribe);
+ return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, diskMonitorJobService.listJobs(param), methodDescribe);
+ }
+
+ @OperateInfo(info = LogEnum.SYSTEM_COMMON)
+ @GetMapping("/job/{jobId}/detail")
+ @ApiOperation("查询磁盘监控任务详情")
+ @ApiImplicitParam(name = "jobId", value = "任务ID", required = true)
+ public HttpResult jobDetail(@PathVariable("jobId") Long jobId) {
+ String methodDescribe = getMethodDescribe("jobDetail");
+ LogUtil.njcnDebug(log, "{},查询磁盘监控任务详情,jobId={}", methodDescribe, jobId);
+ return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, diskMonitorJobService.getJobDetail(jobId), methodDescribe);
+ }
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/controller/DiskMonitorNotifyController.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/controller/DiskMonitorNotifyController.java
new file mode 100644
index 0000000..7cba386
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/controller/DiskMonitorNotifyController.java
@@ -0,0 +1,49 @@
+package com.njcn.gather.systemmonitor.disk.controller;
+
+import com.njcn.common.pojo.annotation.OperateInfo;
+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.common.utils.LogUtil;
+import com.njcn.gather.systemmonitor.disk.pojo.param.DiskMonitorParam;
+import com.njcn.gather.systemmonitor.disk.service.IDiskMonitorNotifyService;
+import com.njcn.web.controller.BaseController;
+import com.njcn.web.utils.HttpResultUtil;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+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.RestController;
+
+import javax.validation.Valid;
+
+/**
+ * 磁盘监控通知接口。
+ */
+@Validated
+@Slf4j
+@Api(tags = "磁盘监控通知")
+@RestController
+@RequestMapping("/disk-monitor")
+@RequiredArgsConstructor
+public class DiskMonitorNotifyController extends BaseController {
+
+ private final IDiskMonitorNotifyService diskMonitorNotifyService;
+
+ @OperateInfo(info = LogEnum.SYSTEM_COMMON, operateType = OperateType.UPDATE)
+ @PostMapping("/notify/test")
+ @ApiOperation("测试磁盘监控通知")
+ @ApiImplicitParam(name = "param", value = "通知测试参数", required = true)
+ public HttpResult testNotify(@RequestBody @Valid DiskMonitorParam.NotifyTestParam param) {
+ String methodDescribe = getMethodDescribe("testNotify");
+ LogUtil.njcnDebug(log, "{},测试磁盘监控通知,driveLetter={}", methodDescribe, param.getDriveLetter());
+ boolean result = diskMonitorNotifyService.testNotify(param);
+ return HttpResultUtil.assembleCommonResponseResult(result ? CommonResponseEnum.SUCCESS : CommonResponseEnum.FAIL, result, methodDescribe);
+ }
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/controller/DiskMonitorPolicyController.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/controller/DiskMonitorPolicyController.java
new file mode 100644
index 0000000..8487ae1
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/controller/DiskMonitorPolicyController.java
@@ -0,0 +1,60 @@
+package com.njcn.gather.systemmonitor.disk.controller;
+
+import com.njcn.common.pojo.annotation.OperateInfo;
+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.common.utils.LogUtil;
+import com.njcn.gather.systemmonitor.disk.pojo.param.DiskMonitorParam;
+import com.njcn.gather.systemmonitor.disk.pojo.vo.DiskMonitorVO;
+import com.njcn.gather.systemmonitor.disk.service.IDiskMonitorPolicyService;
+import com.njcn.web.controller.BaseController;
+import com.njcn.web.utils.HttpResultUtil;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+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.RestController;
+
+import javax.validation.Valid;
+
+/**
+ * 磁盘监控配置接口。
+ */
+@Validated
+@Slf4j
+@Api(tags = "磁盘监控配置")
+@RestController
+@RequestMapping("/disk-monitor")
+@RequiredArgsConstructor
+public class DiskMonitorPolicyController extends BaseController {
+
+ private final IDiskMonitorPolicyService diskMonitorPolicyService;
+
+ @OperateInfo(info = LogEnum.SYSTEM_COMMON)
+ @GetMapping("/policy/detail")
+ @ApiOperation("查询磁盘监控配置详情")
+ public HttpResult detail() {
+ String methodDescribe = getMethodDescribe("detail");
+ LogUtil.njcnDebug(log, "{},查询磁盘监控配置详情", methodDescribe);
+ return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, diskMonitorPolicyService.getPolicyDetail(), methodDescribe);
+ }
+
+ @OperateInfo(info = LogEnum.SYSTEM_COMMON, operateType = OperateType.UPDATE)
+ @PostMapping("/policy/save")
+ @ApiOperation("保存磁盘监控配置")
+ @ApiImplicitParam(name = "param", value = "磁盘监控配置", required = true)
+ public HttpResult save(@RequestBody @Valid DiskMonitorParam.PolicySaveParam param) {
+ String methodDescribe = getMethodDescribe("save");
+ LogUtil.njcnDebug(log, "{},保存磁盘监控配置", methodDescribe);
+ boolean result = diskMonitorPolicyService.savePolicy(param);
+ return HttpResultUtil.assembleCommonResponseResult(result ? CommonResponseEnum.SUCCESS : CommonResponseEnum.FAIL, result, methodDescribe);
+ }
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/enums/DiskMonitorResponseEnum.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/enums/DiskMonitorResponseEnum.java
new file mode 100644
index 0000000..74c4001
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/enums/DiskMonitorResponseEnum.java
@@ -0,0 +1,37 @@
+package com.njcn.gather.systemmonitor.disk.enums;
+
+import lombok.Getter;
+
+/**
+ * 磁盘监控模块业务响应码。
+ */
+@Getter
+public enum DiskMonitorResponseEnum {
+
+ POLICY_NOT_FOUND("A01060", "磁盘监控策略不存在"),
+ TARGET_NOT_FOUND("A01061", "磁盘监控盘符配置不存在"),
+ DRIVE_LETTER_REPEAT("A01062", "盘符重复,请检查driveLetter参数"),
+ DRIVE_LETTER_FORMAT_ERROR("A01063", "盘符格式错误,请使用类似 C: 的格式"),
+ DAILY_RUN_TIME_FORMAT_ERROR("A01064", "每日统一执行时间格式错误,请使用HH:mm:ss"),
+ USAGE_PERCENT_INVALID("A01065", "告警使用率必须大于等于预警使用率"),
+ NOTIFY_PATH_EMPTY("A01066", "路径通知目标不能为空"),
+ NOTIFY_HTTP_EMPTY("A01067", "HTTP通知目标不能为空"),
+ NOTIFY_PATH_VALUE_EMPTY("A01068", "通知路径不能为空"),
+ NOTIFY_HTTP_URL_INVALID("A01069", "HTTP通知目标格式错误,请检查url参数"),
+ NO_ENABLED_TARGET("A01070", "暂无启用的磁盘监控盘符配置"),
+ JOB_NOT_FOUND("A01071", "监控任务不存在"),
+ POLICY_SAVE_FAILED("A01072", "磁盘监控配置保存失败"),
+ DRIVE_SCAN_FAILED("A01073", "磁盘扫描失败"),
+ NOTIFY_TARGET_NOT_FOUND("A01074", "通知测试盘符不存在"),
+ NOTIFY_CHANNEL_MISSING("A01075", "当前盘符未配置可用通知通道"),
+ POLICY_MODE_INVALID("A01076", "通知模式配置非法"),
+ JOB_SOURCE_INVALID("A01077", "任务来源非法");
+
+ private final String code;
+ private final String message;
+
+ DiskMonitorResponseEnum(String code, String message) {
+ this.code = code;
+ this.message = message;
+ }
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/event/DiskMonitorPolicyChangedEvent.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/event/DiskMonitorPolicyChangedEvent.java
new file mode 100644
index 0000000..633d2a3
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/event/DiskMonitorPolicyChangedEvent.java
@@ -0,0 +1,7 @@
+package com.njcn.gather.systemmonitor.disk.event;
+
+/**
+ * 磁盘监控策略变更事件。
+ */
+public class DiskMonitorPolicyChangedEvent {
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/DiskMonitorJobMapper.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/DiskMonitorJobMapper.java
new file mode 100644
index 0000000..b713ad9
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/DiskMonitorJobMapper.java
@@ -0,0 +1,10 @@
+package com.njcn.gather.systemmonitor.disk.mapper;
+
+import com.github.yulichang.base.MPJBaseMapper;
+import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorJob;
+
+/**
+ * 磁盘监控任务 Mapper。
+ */
+public interface DiskMonitorJobMapper extends MPJBaseMapper {
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/DiskMonitorNotifyLogMapper.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/DiskMonitorNotifyLogMapper.java
new file mode 100644
index 0000000..8c7970c
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/DiskMonitorNotifyLogMapper.java
@@ -0,0 +1,10 @@
+package com.njcn.gather.systemmonitor.disk.mapper;
+
+import com.github.yulichang.base.MPJBaseMapper;
+import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorNotifyLog;
+
+/**
+ * 磁盘监控通知日志 Mapper。
+ */
+public interface DiskMonitorNotifyLogMapper extends MPJBaseMapper {
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/DiskMonitorPolicyMapper.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/DiskMonitorPolicyMapper.java
new file mode 100644
index 0000000..2e01dc6
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/DiskMonitorPolicyMapper.java
@@ -0,0 +1,10 @@
+package com.njcn.gather.systemmonitor.disk.mapper;
+
+import com.github.yulichang.base.MPJBaseMapper;
+import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorPolicy;
+
+/**
+ * 磁盘监控策略 Mapper。
+ */
+public interface DiskMonitorPolicyMapper extends MPJBaseMapper {
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/DiskMonitorResultMapper.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/DiskMonitorResultMapper.java
new file mode 100644
index 0000000..980560c
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/DiskMonitorResultMapper.java
@@ -0,0 +1,10 @@
+package com.njcn.gather.systemmonitor.disk.mapper;
+
+import com.github.yulichang.base.MPJBaseMapper;
+import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorResult;
+
+/**
+ * 磁盘监控结果 Mapper。
+ */
+public interface DiskMonitorResultMapper extends MPJBaseMapper {
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/DiskMonitorTargetMapper.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/DiskMonitorTargetMapper.java
new file mode 100644
index 0000000..b3b1c02
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/DiskMonitorTargetMapper.java
@@ -0,0 +1,10 @@
+package com.njcn.gather.systemmonitor.disk.mapper;
+
+import com.github.yulichang.base.MPJBaseMapper;
+import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorTarget;
+
+/**
+ * 磁盘监控盘符配置 Mapper。
+ */
+public interface DiskMonitorTargetMapper extends MPJBaseMapper {
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/mapping/DiskMonitorJobMapper.xml b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/mapping/DiskMonitorJobMapper.xml
new file mode 100644
index 0000000..c1620f1
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/mapping/DiskMonitorJobMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/mapping/DiskMonitorNotifyLogMapper.xml b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/mapping/DiskMonitorNotifyLogMapper.xml
new file mode 100644
index 0000000..409e8b1
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/mapping/DiskMonitorNotifyLogMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/mapping/DiskMonitorPolicyMapper.xml b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/mapping/DiskMonitorPolicyMapper.xml
new file mode 100644
index 0000000..7b964d0
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/mapping/DiskMonitorPolicyMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/mapping/DiskMonitorResultMapper.xml b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/mapping/DiskMonitorResultMapper.xml
new file mode 100644
index 0000000..ce0d629
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/mapping/DiskMonitorResultMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/mapping/DiskMonitorTargetMapper.xml b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/mapping/DiskMonitorTargetMapper.xml
new file mode 100644
index 0000000..228061a
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/mapper/mapping/DiskMonitorTargetMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/dto/DiskMonitorNotifyHttpItem.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/dto/DiskMonitorNotifyHttpItem.java
new file mode 100644
index 0000000..c4717bb
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/dto/DiskMonitorNotifyHttpItem.java
@@ -0,0 +1,31 @@
+package com.njcn.gather.systemmonitor.disk.pojo.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotBlank;
+
+/**
+ * HTTP 通知项。
+ */
+@Data
+public class DiskMonitorNotifyHttpItem {
+
+ @ApiModelProperty("回调地址")
+ @NotBlank(message = "HTTP通知目标不能为空,请检查url参数")
+ private String url;
+
+ @ApiModelProperty("名称")
+ private String name;
+
+ @ApiModelProperty("请求方法")
+ private String method;
+
+ @ApiModelProperty("超时时间")
+ @Min(value = 1, message = "timeoutMs必须大于0")
+ private Integer timeoutMs;
+
+ @ApiModelProperty("是否启用")
+ private Boolean enabled;
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/dto/DiskMonitorNotifyPathItem.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/dto/DiskMonitorNotifyPathItem.java
new file mode 100644
index 0000000..4fb6db4
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/dto/DiskMonitorNotifyPathItem.java
@@ -0,0 +1,23 @@
+package com.njcn.gather.systemmonitor.disk.pojo.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * 路径通知项。
+ */
+@Data
+public class DiskMonitorNotifyPathItem {
+
+ @ApiModelProperty("路径")
+ @NotBlank(message = "通知路径不能为空,请检查path参数")
+ private String path;
+
+ @ApiModelProperty("名称")
+ private String name;
+
+ @ApiModelProperty("是否启用")
+ private Boolean enabled;
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/param/DiskMonitorParam.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/param/DiskMonitorParam.java
new file mode 100644
index 0000000..36ed66c
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/param/DiskMonitorParam.java
@@ -0,0 +1,131 @@
+package com.njcn.gather.systemmonitor.disk.pojo.param;
+
+import com.njcn.gather.systemmonitor.disk.constant.DiskMonitorValidMessage;
+import com.njcn.gather.systemmonitor.disk.pojo.dto.DiskMonitorNotifyHttpItem;
+import com.njcn.gather.systemmonitor.disk.pojo.dto.DiskMonitorNotifyPathItem;
+import com.njcn.web.pojo.param.BaseParam;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.Valid;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 磁盘监控接口参数。
+ */
+public class DiskMonitorParam {
+
+ @Data
+ public static class PolicySaveParam {
+
+ @ApiModelProperty("全局策略")
+ @NotNull(message = DiskMonitorValidMessage.POLICY_NOT_NULL)
+ @Valid
+ private PolicyParam policy;
+
+ @ApiModelProperty("盘符配置")
+ @NotNull(message = DiskMonitorValidMessage.TARGETS_NOT_NULL)
+ @Valid
+ private List targets = new ArrayList<>();
+ }
+
+ @Data
+ public static class PolicyParam {
+
+ @ApiModelProperty("策略ID")
+ private Long id;
+
+ @ApiModelProperty("策略名称")
+ @NotBlank(message = DiskMonitorValidMessage.POLICY_NAME_NOT_BLANK)
+ private String policyName;
+
+ @ApiModelProperty("是否启用监控")
+ private Boolean monitorEnabled;
+
+ @ApiModelProperty("应用启动后立即执行一次")
+ private Boolean runOnAppStart;
+
+ @ApiModelProperty("每日统一执行时间")
+ @NotBlank(message = DiskMonitorValidMessage.DAILY_RUN_TIME_NOT_BLANK)
+ private String dailyRunTime;
+
+ @ApiModelProperty("预警通知模式")
+ private String warningNotifyMode;
+
+ @ApiModelProperty("告警通知模式")
+ private String alarmNotifyMode;
+
+ @ApiModelProperty("备注")
+ private String remark;
+ }
+
+ @Data
+ public static class TargetParam {
+
+ @ApiModelProperty("盘符配置ID")
+ private Long id;
+
+ @ApiModelProperty("盘符")
+ @NotBlank(message = DiskMonitorValidMessage.DRIVE_LETTER_NOT_BLANK)
+ private String driveLetter;
+
+ @ApiModelProperty("是否监控")
+ private Boolean monitorEnabled;
+
+ @ApiModelProperty("预警使用率")
+ @NotNull(message = DiskMonitorValidMessage.WARNING_USAGE_PERCENT_NOT_NULL)
+ @Min(value = 1, message = DiskMonitorValidMessage.USAGE_PERCENT_FORMAT_ERROR)
+ @Max(value = 100, message = DiskMonitorValidMessage.USAGE_PERCENT_FORMAT_ERROR)
+ private Integer warningUsagePercent;
+
+ @ApiModelProperty("告警使用率")
+ @NotNull(message = DiskMonitorValidMessage.ALARM_USAGE_PERCENT_NOT_NULL)
+ @Min(value = 1, message = DiskMonitorValidMessage.USAGE_PERCENT_FORMAT_ERROR)
+ @Max(value = 100, message = DiskMonitorValidMessage.USAGE_PERCENT_FORMAT_ERROR)
+ private Integer alarmUsagePercent;
+
+ @ApiModelProperty("是否启用路径通知")
+ private Boolean notifyPathEnabled;
+
+ @ApiModelProperty("路径通知列表")
+ @Valid
+ private List notifyPathList = new ArrayList<>();
+
+ @ApiModelProperty("是否启用HTTP通知")
+ private Boolean notifyHttpEnabled;
+
+ @ApiModelProperty("HTTP通知列表")
+ @Valid
+ private List notifyHttpList = new ArrayList<>();
+
+ @ApiModelProperty("备注")
+ private String remark;
+ }
+
+ @Data
+ public static class JobRunParam {
+
+ @ApiModelProperty("任务来源")
+ @NotBlank(message = DiskMonitorValidMessage.JOB_SOURCE_NOT_BLANK)
+ private String jobSource;
+ }
+
+ @Data
+ @EqualsAndHashCode(callSuper = true)
+ public static class JobListParam extends BaseParam {
+ }
+
+ @Data
+ public static class NotifyTestParam {
+
+ @ApiModelProperty("盘符")
+ @NotBlank(message = DiskMonitorValidMessage.DRIVE_LETTER_NOT_BLANK)
+ private String driveLetter;
+ }
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/po/DiskMonitorJob.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/po/DiskMonitorJob.java
new file mode 100644
index 0000000..6f52f43
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/po/DiskMonitorJob.java
@@ -0,0 +1,58 @@
+package com.njcn.gather.systemmonitor.disk.pojo.po;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 磁盘监控任务批次表。
+ */
+@Data
+@TableName("disk_monitor_job")
+public class DiskMonitorJob implements Serializable {
+ private static final long serialVersionUID = 2956749403195165256L;
+
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ @TableField("job_no")
+ private String jobNo;
+
+ @TableField("job_source")
+ private String jobSource;
+
+ @TableField("planned_time")
+ private LocalDateTime plannedTime;
+
+ @TableField("started_at")
+ private LocalDateTime startedAt;
+
+ @TableField("finished_at")
+ private LocalDateTime finishedAt;
+
+ @TableField("job_status")
+ private String jobStatus;
+
+ @TableField("target_count")
+ private Integer targetCount;
+
+ @TableField("success_count")
+ private Integer successCount;
+
+ @TableField("warning_count")
+ private Integer warningCount;
+
+ @TableField("alarm_count")
+ private Integer alarmCount;
+
+ @TableField("message")
+ private String message;
+
+ @TableField("created_at")
+ private LocalDateTime createdAt;
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/po/DiskMonitorNotifyLog.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/po/DiskMonitorNotifyLog.java
new file mode 100644
index 0000000..ae8a920
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/po/DiskMonitorNotifyLog.java
@@ -0,0 +1,58 @@
+package com.njcn.gather.systemmonitor.disk.pojo.po;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 磁盘监控通知日志表。
+ */
+@Data
+@TableName("disk_monitor_notify_log")
+public class DiskMonitorNotifyLog implements Serializable {
+ private static final long serialVersionUID = -8873612319853902451L;
+
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ @TableField("job_id")
+ private Long jobId;
+
+ @TableField("result_id")
+ private Long resultId;
+
+ @TableField("target_id")
+ private Long targetId;
+
+ @TableField("drive_letter")
+ private String driveLetter;
+
+ @TableField("notify_level")
+ private String notifyLevel;
+
+ @TableField("channel_type")
+ private String channelType;
+
+ @TableField("channel_target")
+ private String channelTarget;
+
+ @TableField("notify_title")
+ private String notifyTitle;
+
+ @TableField("notify_content")
+ private String notifyContent;
+
+ @TableField("send_status")
+ private String sendStatus;
+
+ @TableField("response_message")
+ private String responseMessage;
+
+ @TableField("sent_at")
+ private LocalDateTime sentAt;
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/po/DiskMonitorPolicy.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/po/DiskMonitorPolicy.java
new file mode 100644
index 0000000..f362aec
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/po/DiskMonitorPolicy.java
@@ -0,0 +1,59 @@
+package com.njcn.gather.systemmonitor.disk.pojo.po;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+
+/**
+ * 磁盘监控全局策略表。
+ */
+@Data
+@TableName("disk_monitor_policy")
+public class DiskMonitorPolicy implements Serializable {
+ private static final long serialVersionUID = -2789228200584651740L;
+
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ @TableField("policy_name")
+ private String policyName;
+
+ @TableField("monitor_enabled")
+ private Integer monitorEnabled;
+
+ @TableField("run_on_app_start")
+ private Integer runOnAppStart;
+
+ @TableField("daily_run_time")
+ private LocalTime dailyRunTime;
+
+ @TableField("warning_notify_mode")
+ private String warningNotifyMode;
+
+ @TableField("alarm_notify_mode")
+ private String alarmNotifyMode;
+
+ @TableField("last_job_id")
+ private Long lastJobId;
+
+ @TableField("remark")
+ private String remark;
+
+ @TableField("created_by")
+ private String createdBy;
+
+ @TableField("created_at")
+ private LocalDateTime createdAt;
+
+ @TableField("updated_by")
+ private String updatedBy;
+
+ @TableField("updated_at")
+ private LocalDateTime updatedAt;
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/po/DiskMonitorResult.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/po/DiskMonitorResult.java
new file mode 100644
index 0000000..530fdf0
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/po/DiskMonitorResult.java
@@ -0,0 +1,65 @@
+package com.njcn.gather.systemmonitor.disk.pojo.po;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 磁盘监控结果表。
+ */
+@Data
+@TableName("disk_monitor_result")
+public class DiskMonitorResult implements Serializable {
+ private static final long serialVersionUID = 5557836396879348480L;
+
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ @TableField("job_id")
+ private Long jobId;
+
+ @TableField("target_id")
+ private Long targetId;
+
+ @TableField("drive_letter")
+ private String driveLetter;
+
+ @TableField("total_bytes")
+ private Long totalBytes;
+
+ @TableField("used_bytes")
+ private Long usedBytes;
+
+ @TableField("free_bytes")
+ private Long freeBytes;
+
+ @TableField("used_percent")
+ private BigDecimal usedPercent;
+
+ @TableField("current_status")
+ private String currentStatus;
+
+ @TableField("previous_status")
+ private String previousStatus;
+
+ @TableField("status_changed")
+ private Integer statusChanged;
+
+ @TableField("should_notify")
+ private Integer shouldNotify;
+
+ @TableField("notify_reason")
+ private String notifyReason;
+
+ @TableField("scan_time")
+ private LocalDateTime scanTime;
+
+ @TableField("message")
+ private String message;
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/po/DiskMonitorTarget.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/po/DiskMonitorTarget.java
new file mode 100644
index 0000000..23f24ad
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/po/DiskMonitorTarget.java
@@ -0,0 +1,74 @@
+package com.njcn.gather.systemmonitor.disk.pojo.po;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 磁盘监控盘符配置表。
+ */
+@Data
+@TableName("disk_monitor_target")
+public class DiskMonitorTarget implements Serializable {
+ private static final long serialVersionUID = -3567440027709476481L;
+
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ @TableField("policy_id")
+ private Long policyId;
+
+ @TableField("drive_letter")
+ private String driveLetter;
+
+ @TableField("monitor_enabled")
+ private Integer monitorEnabled;
+
+ @TableField("warning_usage_percent")
+ private Integer warningUsagePercent;
+
+ @TableField("alarm_usage_percent")
+ private Integer alarmUsagePercent;
+
+ @TableField("notify_path_enabled")
+ private Integer notifyPathEnabled;
+
+ @TableField("notify_path_list_json")
+ private String notifyPathListJson;
+
+ @TableField("notify_http_enabled")
+ private Integer notifyHttpEnabled;
+
+ @TableField("notify_http_list_json")
+ private String notifyHttpListJson;
+
+ @TableField("last_status")
+ private String lastStatus;
+
+ @TableField("last_scan_time")
+ private LocalDateTime lastScanTime;
+
+ @TableField("last_used_percent")
+ private BigDecimal lastUsedPercent;
+
+ @TableField("remark")
+ private String remark;
+
+ @TableField("created_by")
+ private String createdBy;
+
+ @TableField("created_at")
+ private LocalDateTime createdAt;
+
+ @TableField("updated_by")
+ private String updatedBy;
+
+ @TableField("updated_at")
+ private LocalDateTime updatedAt;
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/vo/DiskMonitorVO.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/vo/DiskMonitorVO.java
new file mode 100644
index 0000000..5ae233f
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/pojo/vo/DiskMonitorVO.java
@@ -0,0 +1,124 @@
+package com.njcn.gather.systemmonitor.disk.pojo.vo;
+
+import com.njcn.gather.systemmonitor.disk.pojo.dto.DiskMonitorNotifyHttpItem;
+import com.njcn.gather.systemmonitor.disk.pojo.dto.DiskMonitorNotifyPathItem;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 磁盘监控接口返回对象。
+ */
+public class DiskMonitorVO {
+
+ @Data
+ public static class PolicyDetailVO {
+ private PolicyVO policy;
+ private List targets = new ArrayList<>();
+ }
+
+ @Data
+ public static class PolicyVO {
+ private Long id;
+ private String policyName;
+ private Boolean monitorEnabled;
+ private Boolean runOnAppStart;
+ private String dailyRunTime;
+ private String warningNotifyMode;
+ private String alarmNotifyMode;
+ private Long lastJobId;
+ private String remark;
+ }
+
+ @Data
+ public static class TargetVO {
+ private Long id;
+ private String driveLetter;
+ private Boolean monitorEnabled;
+ private Integer warningUsagePercent;
+ private Integer alarmUsagePercent;
+ private Boolean notifyPathEnabled;
+ private List notifyPathList = new ArrayList<>();
+ private Boolean notifyHttpEnabled;
+ private List notifyHttpList = new ArrayList<>();
+ private String lastStatus;
+ private String lastScanTime;
+ private BigDecimal lastUsedPercent;
+ private String remark;
+ }
+
+ @Data
+ public static class JobRunVO {
+ private Long jobId;
+ private String jobNo;
+ }
+
+ @Data
+ public static class JobListVO {
+ private Long jobId;
+ private String jobNo;
+ private String jobSource;
+ private String startedAt;
+ private String finishedAt;
+ private String jobStatus;
+ private Integer targetCount;
+ private Integer warningCount;
+ private Integer alarmCount;
+ private String message;
+ }
+
+ @Data
+ public static class JobDetailVO {
+ private JobInfoVO job;
+ private List results = new ArrayList<>();
+ private List notifyLogs = new ArrayList<>();
+ }
+
+ @Data
+ public static class JobInfoVO {
+ private Long id;
+ private String jobNo;
+ private String jobSource;
+ private String startedAt;
+ private String finishedAt;
+ private String jobStatus;
+ private Integer targetCount;
+ private Integer successCount;
+ private Integer warningCount;
+ private Integer alarmCount;
+ private String message;
+ }
+
+ @Data
+ public static class ResultVO {
+ private Long resultId;
+ private Long targetId;
+ private String driveLetter;
+ private Long totalBytes;
+ private Long usedBytes;
+ private Long freeBytes;
+ private BigDecimal usedPercent;
+ private String currentStatus;
+ private String previousStatus;
+ private Boolean statusChanged;
+ private Boolean shouldNotify;
+ private String notifyReason;
+ private String scanTime;
+ private String message;
+ }
+
+ @Data
+ public static class NotifyLogVO {
+ private Long id;
+ private Long resultId;
+ private String driveLetter;
+ private String notifyLevel;
+ private String channelType;
+ private String channelTarget;
+ private String sendStatus;
+ private String responseMessage;
+ private String sentAt;
+ }
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/schedule/DiskMonitorScheduleManager.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/schedule/DiskMonitorScheduleManager.java
new file mode 100644
index 0000000..f48005f
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/schedule/DiskMonitorScheduleManager.java
@@ -0,0 +1,94 @@
+package com.njcn.gather.systemmonitor.disk.schedule;
+
+import com.njcn.gather.systemmonitor.disk.event.DiskMonitorPolicyChangedEvent;
+import com.njcn.gather.systemmonitor.disk.service.IDiskMonitorJobService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PreDestroy;
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * 磁盘监控启动与定时调度管理。
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class DiskMonitorScheduleManager implements ApplicationRunner {
+
+ private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+ private final IDiskMonitorJobService diskMonitorJobService;
+ private final AtomicInteger threadIndex = new AtomicInteger(1);
+ private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(createThreadFactory());
+ private ScheduledFuture> scheduledFuture;
+
+ @Override
+ public void run(ApplicationArguments args) {
+ try {
+ diskMonitorJobService.executeAppStartMonitor();
+ } catch (Exception exception) {
+ log.error("应用启动后执行磁盘监控失败", exception);
+ }
+ refreshSchedule();
+ }
+
+ @EventListener(DiskMonitorPolicyChangedEvent.class)
+ public void onPolicyChanged() {
+ refreshSchedule();
+ }
+
+ public synchronized void refreshSchedule() {
+ if (scheduledFuture != null) {
+ scheduledFuture.cancel(false);
+ scheduledFuture = null;
+ }
+ LocalDateTime nextRunTime = diskMonitorJobService.getNextDailyRunTime();
+ if (nextRunTime == null) {
+ log.info("磁盘监控每日调度未启用或未配置执行时间,跳过注册");
+ return;
+ }
+ long delayMs = Math.max(Duration.between(LocalDateTime.now(), nextRunTime).toMillis(), 0L);
+ scheduledFuture = scheduler.schedule(this::executeDailyTask, delayMs, TimeUnit.MILLISECONDS);
+ log.info("磁盘监控每日调度已注册,下次执行时间:{}", nextRunTime.format(DATE_TIME_FORMATTER));
+ }
+
+ private void executeDailyTask() {
+ try {
+ diskMonitorJobService.executeDailyScheduleMonitor();
+ } catch (Exception exception) {
+ log.error("每日磁盘监控执行失败", exception);
+ } finally {
+ refreshSchedule();
+ }
+ }
+
+ private ThreadFactory createThreadFactory() {
+ return runnable -> {
+ Thread thread = new Thread(runnable);
+ thread.setName("disk-monitor-schedule-" + threadIndex.getAndIncrement());
+ thread.setDaemon(true);
+ return thread;
+ };
+ }
+
+ @PreDestroy
+ public synchronized void destroy() {
+ if (scheduledFuture != null) {
+ scheduledFuture.cancel(false);
+ }
+ scheduler.shutdown();
+ }
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/IDiskMonitorJobService.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/IDiskMonitorJobService.java
new file mode 100644
index 0000000..6671ecc
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/IDiskMonitorJobService.java
@@ -0,0 +1,27 @@
+package com.njcn.gather.systemmonitor.disk.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.njcn.gather.systemmonitor.disk.pojo.param.DiskMonitorParam;
+import com.njcn.gather.systemmonitor.disk.pojo.vo.DiskMonitorVO;
+
+import java.time.LocalDateTime;
+
+/**
+ * 磁盘监控任务服务。
+ */
+public interface IDiskMonitorJobService {
+
+ DiskMonitorVO.JobRunVO runJob(DiskMonitorParam.JobRunParam param);
+
+ Page listJobs(DiskMonitorParam.JobListParam param);
+
+ DiskMonitorVO.JobDetailVO getJobDetail(Long jobId);
+
+ void executeAppStartMonitor();
+
+ void executeDailyScheduleMonitor();
+
+ LocalDateTime getNextDailyRunTime();
+
+ void executeNotifyTest(String driveLetter);
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/IDiskMonitorNotifyService.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/IDiskMonitorNotifyService.java
new file mode 100644
index 0000000..e740501
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/IDiskMonitorNotifyService.java
@@ -0,0 +1,11 @@
+package com.njcn.gather.systemmonitor.disk.service;
+
+import com.njcn.gather.systemmonitor.disk.pojo.param.DiskMonitorParam;
+
+/**
+ * 磁盘监控通知服务。
+ */
+public interface IDiskMonitorNotifyService {
+
+ boolean testNotify(DiskMonitorParam.NotifyTestParam param);
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/IDiskMonitorPolicyService.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/IDiskMonitorPolicyService.java
new file mode 100644
index 0000000..c99350e
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/IDiskMonitorPolicyService.java
@@ -0,0 +1,26 @@
+package com.njcn.gather.systemmonitor.disk.service;
+
+import com.njcn.gather.systemmonitor.disk.pojo.param.DiskMonitorParam;
+import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorPolicy;
+import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorTarget;
+import com.njcn.gather.systemmonitor.disk.pojo.vo.DiskMonitorVO;
+
+import java.util.List;
+
+/**
+ * 磁盘监控配置服务。
+ */
+public interface IDiskMonitorPolicyService {
+
+ DiskMonitorVO.PolicyDetailVO getPolicyDetail();
+
+ boolean savePolicy(DiskMonitorParam.PolicySaveParam param);
+
+ DiskMonitorPolicy getCurrentPolicy();
+
+ DiskMonitorPolicy getOrCreatePolicy();
+
+ List listEnabledTargets();
+
+ DiskMonitorTarget getTargetByDriveLetter(String driveLetter);
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/impl/DiskMonitorJobServiceImpl.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/impl/DiskMonitorJobServiceImpl.java
new file mode 100644
index 0000000..65d51b0
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/impl/DiskMonitorJobServiceImpl.java
@@ -0,0 +1,517 @@
+package com.njcn.gather.systemmonitor.disk.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.njcn.common.pojo.exception.BusinessException;
+import com.njcn.gather.systemmonitor.disk.component.DiskMonitorNotificationComponent;
+import com.njcn.gather.systemmonitor.disk.constant.DiskMonitorConstant;
+import com.njcn.gather.systemmonitor.disk.enums.DiskMonitorResponseEnum;
+import com.njcn.gather.systemmonitor.disk.mapper.DiskMonitorJobMapper;
+import com.njcn.gather.systemmonitor.disk.mapper.DiskMonitorNotifyLogMapper;
+import com.njcn.gather.systemmonitor.disk.mapper.DiskMonitorPolicyMapper;
+import com.njcn.gather.systemmonitor.disk.mapper.DiskMonitorResultMapper;
+import com.njcn.gather.systemmonitor.disk.mapper.DiskMonitorTargetMapper;
+import com.njcn.gather.systemmonitor.disk.pojo.param.DiskMonitorParam;
+import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorJob;
+import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorNotifyLog;
+import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorPolicy;
+import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorResult;
+import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorTarget;
+import com.njcn.gather.systemmonitor.disk.pojo.vo.DiskMonitorVO;
+import com.njcn.gather.systemmonitor.disk.service.IDiskMonitorJobService;
+import com.njcn.gather.systemmonitor.disk.service.IDiskMonitorPolicyService;
+import com.njcn.web.factory.PageFactory;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.io.File;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * 磁盘监控任务服务实现。
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class DiskMonitorJobServiceImpl implements IDiskMonitorJobService {
+
+ private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+ private final IDiskMonitorPolicyService diskMonitorPolicyService;
+ private final DiskMonitorNotificationComponent diskMonitorNotificationComponent;
+ private final DiskMonitorJobMapper jobMapper;
+ private final DiskMonitorResultMapper resultMapper;
+ private final DiskMonitorNotifyLogMapper notifyLogMapper;
+ private final DiskMonitorTargetMapper targetMapper;
+ private final DiskMonitorPolicyMapper policyMapper;
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public DiskMonitorVO.JobRunVO runJob(DiskMonitorParam.JobRunParam param) {
+ String jobSource = StrUtil.blankToDefault(param.getJobSource(), DiskMonitorConstant.JOB_SOURCE_MANUAL).trim().toUpperCase();
+ if (!DiskMonitorConstant.JOB_SOURCE_MANUAL.equals(jobSource)) {
+ throw new BusinessException(DiskMonitorResponseEnum.JOB_SOURCE_INVALID);
+ }
+ return executeMonitorJob(jobSource, null, false, true);
+ }
+
+ @Override
+ public Page listJobs(DiskMonitorParam.JobListParam param) {
+ Page page = jobMapper.selectPage(
+ new Page<>(PageFactory.getPageNum(param), PageFactory.getPageSize(param)),
+ new QueryWrapper().orderByDesc("started_at", "id")
+ );
+ Page resultPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
+ resultPage.setRecords(page.getRecords().stream().map(this::toJobListVO).collect(Collectors.toList()));
+ return resultPage;
+ }
+
+ @Override
+ public DiskMonitorVO.JobDetailVO getJobDetail(Long jobId) {
+ DiskMonitorJob job = jobMapper.selectById(jobId);
+ if (job == null) {
+ throw new BusinessException(DiskMonitorResponseEnum.JOB_NOT_FOUND);
+ }
+ DiskMonitorVO.JobDetailVO detailVO = new DiskMonitorVO.JobDetailVO();
+ detailVO.setJob(toJobInfoVO(job));
+ List results = resultMapper.selectList(new QueryWrapper()
+ .eq("job_id", jobId)
+ .orderByAsc("scan_time", "id"));
+ detailVO.setResults(results.stream().map(this::toResultVO).collect(Collectors.toList()));
+ List notifyLogs = notifyLogMapper.selectList(new QueryWrapper()
+ .eq("job_id", jobId)
+ .orderByAsc("sent_at", "id"));
+ detailVO.setNotifyLogs(notifyLogs.stream().map(this::toNotifyLogVO).collect(Collectors.toList()));
+ return detailVO;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void executeAppStartMonitor() {
+ DiskMonitorPolicy policy = diskMonitorPolicyService.getOrCreatePolicy();
+ if (!isEnabled(policy.getMonitorEnabled()) || !isEnabled(policy.getRunOnAppStart())) {
+ return;
+ }
+ executeMonitorJob(DiskMonitorConstant.JOB_SOURCE_APP_START, null, false, false);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void executeDailyScheduleMonitor() {
+ DiskMonitorPolicy policy = diskMonitorPolicyService.getOrCreatePolicy();
+ if (!isEnabled(policy.getMonitorEnabled()) || policy.getDailyRunTime() == null) {
+ return;
+ }
+ executeMonitorJob(DiskMonitorConstant.JOB_SOURCE_DAILY_SCHEDULE, null, false, false);
+ }
+
+ @Override
+ public LocalDateTime getNextDailyRunTime() {
+ DiskMonitorPolicy policy = diskMonitorPolicyService.getOrCreatePolicy();
+ if (!isEnabled(policy.getMonitorEnabled()) || policy.getDailyRunTime() == null) {
+ return null;
+ }
+ LocalDateTime now = LocalDateTime.now();
+ LocalDateTime candidate = LocalDate.now().atTime(policy.getDailyRunTime());
+ if (!candidate.isAfter(now)) {
+ candidate = candidate.plusDays(1);
+ }
+ return candidate;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void executeNotifyTest(String driveLetter) {
+ executeMonitorJob(DiskMonitorConstant.JOB_SOURCE_MANUAL, driveLetter, true, true);
+ }
+
+ private DiskMonitorVO.JobRunVO executeMonitorJob(String jobSource, String driveLetter, boolean forceNotify, boolean failWhenNoTarget) {
+ validateJobSource(jobSource);
+ List targets = resolveExecutionTargets(driveLetter);
+ if (targets.isEmpty()) {
+ logNoExecutionTargets(jobSource, driveLetter);
+ if (failWhenNoTarget) {
+ throw new BusinessException(DiskMonitorResponseEnum.NO_ENABLED_TARGET);
+ }
+ return null;
+ }
+
+ LocalDateTime now = LocalDateTime.now();
+ DiskMonitorJob job = new DiskMonitorJob();
+ job.setJobNo(generateJobNo());
+ job.setJobSource(jobSource);
+ job.setPlannedTime(now);
+ job.setStartedAt(now);
+ job.setJobStatus(DiskMonitorConstant.JOB_STATUS_RUNNING);
+ job.setTargetCount(targets.size());
+ job.setSuccessCount(0);
+ job.setWarningCount(0);
+ job.setAlarmCount(0);
+ job.setMessage(forceNotify ? "通知测试任务" : null);
+ jobMapper.insert(job);
+
+ int successCount = 0;
+ int warningCount = 0;
+ int alarmCount = 0;
+ List errorMessages = new ArrayList<>();
+ DiskMonitorPolicy policy = diskMonitorPolicyService.getOrCreatePolicy();
+
+ for (DiskMonitorTarget target : targets) {
+ try {
+ ScanSnapshot snapshot = resolveSnapshotStatus(target, scanDrive(target.getDriveLetter()));
+ if (snapshot.isSuccess()) {
+ successCount++;
+ }
+ if (DiskMonitorConstant.STATUS_WARNING.equals(snapshot.getCurrentStatus())) {
+ warningCount++;
+ }
+ if (DiskMonitorConstant.STATUS_ALARM.equals(snapshot.getCurrentStatus())) {
+ alarmCount++;
+ }
+
+ NotifyDecision notifyDecision = resolveNotifyDecision(target, snapshot.getCurrentStatus(), forceNotify);
+ DiskMonitorResult result = buildResult(job, target, snapshot, notifyDecision);
+ resultMapper.insert(result);
+
+ if (notifyDecision.isShouldNotify()) {
+ diskMonitorNotificationComponent.sendNotifications(job, target, result,
+ snapshot.getUsedPercent(), snapshot.getCurrentStatus(),
+ notifyDecision.getNotifyReason(), notifyDecision.getNotifyLevel(),
+ snapshot.getScanTime(), snapshot.getMessage());
+ }
+
+ updateTargetLastState(target, snapshot);
+ } catch (Exception exception) {
+ log.error("磁盘监控执行异常,driveLetter={}", target.getDriveLetter(), exception);
+ errorMessages.add(target.getDriveLetter() + ":" + exception.getMessage());
+ ScanSnapshot failedSnapshot = ScanSnapshot.failed(exception.getMessage());
+ DiskMonitorResult result = buildResult(job, target, failedSnapshot,
+ new NotifyDecision(false, DiskMonitorConstant.NOTIFY_REASON_NO_NOTIFY, resolveNotifyLevel(failedSnapshot.getCurrentStatus())));
+ resultMapper.insert(result);
+ updateTargetLastState(target, failedSnapshot);
+ }
+ }
+
+ finishJob(job, policy, successCount, warningCount, alarmCount, errorMessages);
+ DiskMonitorVO.JobRunVO runVO = new DiskMonitorVO.JobRunVO();
+ runVO.setJobId(job.getId());
+ runVO.setJobNo(job.getJobNo());
+ return runVO;
+ }
+
+ private void finishJob(DiskMonitorJob job, DiskMonitorPolicy policy, int successCount, int warningCount,
+ int alarmCount, List errorMessages) {
+ job.setSuccessCount(successCount);
+ job.setWarningCount(warningCount);
+ job.setAlarmCount(alarmCount);
+ job.setFinishedAt(LocalDateTime.now());
+ if (successCount == job.getTargetCount()) {
+ job.setJobStatus(DiskMonitorConstant.JOB_STATUS_SUCCESS);
+ } else if (successCount == 0) {
+ job.setJobStatus(DiskMonitorConstant.JOB_STATUS_FAILED);
+ } else {
+ job.setJobStatus(DiskMonitorConstant.JOB_STATUS_PARTIAL_SUCCESS);
+ }
+ if (!errorMessages.isEmpty()) {
+ job.setMessage(String.join(";", errorMessages));
+ }
+ jobMapper.updateById(job);
+
+ policy.setLastJobId(job.getId());
+ policyMapper.updateById(policy);
+ }
+
+ private DiskMonitorResult buildResult(DiskMonitorJob job, DiskMonitorTarget target, ScanSnapshot snapshot, NotifyDecision notifyDecision) {
+ DiskMonitorResult result = new DiskMonitorResult();
+ result.setJobId(job.getId());
+ result.setTargetId(target.getId());
+ result.setDriveLetter(target.getDriveLetter());
+ result.setTotalBytes(snapshot.getTotalBytes());
+ result.setUsedBytes(snapshot.getUsedBytes());
+ result.setFreeBytes(snapshot.getFreeBytes());
+ result.setUsedPercent(snapshot.getUsedPercent());
+ result.setCurrentStatus(snapshot.getCurrentStatus());
+ result.setPreviousStatus(resolvePreviousStatus(target.getLastStatus()));
+ result.setStatusChanged(snapshot.getCurrentStatus().equals(resolvePreviousStatus(target.getLastStatus())) ? 0 : 1);
+ result.setShouldNotify(notifyDecision.isShouldNotify() ? 1 : 0);
+ result.setNotifyReason(notifyDecision.getNotifyReason());
+ result.setScanTime(snapshot.getScanTime());
+ result.setMessage(snapshot.getMessage());
+ return result;
+ }
+
+ private void updateTargetLastState(DiskMonitorTarget target, ScanSnapshot snapshot) {
+ target.setLastStatus(snapshot.getCurrentStatus());
+ target.setLastScanTime(snapshot.getScanTime());
+ target.setLastUsedPercent(snapshot.getUsedPercent());
+ targetMapper.updateById(target);
+ }
+
+ private NotifyDecision resolveNotifyDecision(DiskMonitorTarget target, String currentStatus, boolean forceNotify) {
+ String previousStatus = resolvePreviousStatus(target.getLastStatus());
+ if (forceNotify) {
+ return new NotifyDecision(true, DiskMonitorConstant.NOTIFY_REASON_STATUS_CHANGED, resolveNotifyLevel(currentStatus));
+ }
+ if (DiskMonitorConstant.STATUS_ALARM.equals(currentStatus)) {
+ return new NotifyDecision(true, DiskMonitorConstant.NOTIFY_REASON_ALARM_EVERY_TIME, DiskMonitorConstant.NOTIFY_LEVEL_ALARM);
+ }
+ if (DiskMonitorConstant.STATUS_WARNING.equals(currentStatus) && !DiskMonitorConstant.STATUS_WARNING.equals(previousStatus)) {
+ return new NotifyDecision(true, DiskMonitorConstant.NOTIFY_REASON_STATUS_CHANGED, DiskMonitorConstant.NOTIFY_LEVEL_WARNING);
+ }
+ if (DiskMonitorConstant.STATUS_NORMAL.equals(currentStatus)
+ && (DiskMonitorConstant.STATUS_WARNING.equals(previousStatus) || DiskMonitorConstant.STATUS_ALARM.equals(previousStatus))) {
+ return new NotifyDecision(true, DiskMonitorConstant.NOTIFY_REASON_STATUS_CHANGED, DiskMonitorConstant.NOTIFY_LEVEL_RECOVER);
+ }
+ return new NotifyDecision(false, DiskMonitorConstant.NOTIFY_REASON_NO_NOTIFY, resolveNotifyLevel(currentStatus));
+ }
+
+ private String resolveNotifyLevel(String currentStatus) {
+ if (DiskMonitorConstant.STATUS_ALARM.equals(currentStatus)) {
+ return DiskMonitorConstant.NOTIFY_LEVEL_ALARM;
+ }
+ if (DiskMonitorConstant.STATUS_WARNING.equals(currentStatus)) {
+ return DiskMonitorConstant.NOTIFY_LEVEL_WARNING;
+ }
+ return DiskMonitorConstant.NOTIFY_LEVEL_RECOVER;
+ }
+
+ private ScanSnapshot scanDrive(String driveLetter) {
+ File driveRoot = new File(driveLetter + File.separator);
+ if (!driveRoot.exists()) {
+ log.warn("磁盘监控扫描跳过,监控盘符不存在或当前运行账号无访问权限,driveLetter={}", driveLetter);
+ return ScanSnapshot.failed("盘符不存在或当前运行账户无访问权限");
+ }
+ long totalBytes = driveRoot.getTotalSpace();
+ if (totalBytes <= 0) {
+ log.warn("磁盘监控扫描失败,未读取到有效磁盘容量,driveLetter={}", driveLetter);
+ return ScanSnapshot.failed("未读取到有效磁盘容量");
+ }
+ long freeBytes = driveRoot.getUsableSpace();
+ long usedBytes = totalBytes - freeBytes;
+ BigDecimal usedPercent = BigDecimal.valueOf(usedBytes)
+ .multiply(BigDecimal.valueOf(100))
+ .divide(BigDecimal.valueOf(totalBytes), 2, RoundingMode.HALF_UP);
+ return new ScanSnapshot(true, totalBytes, usedBytes, freeBytes, usedPercent, LocalDateTime.now(),
+ DiskMonitorConstant.STATUS_UNKNOWN, null);
+ }
+
+ private ScanSnapshot resolveSnapshotStatus(DiskMonitorTarget target, ScanSnapshot snapshot) {
+ if (!snapshot.isSuccess()) {
+ return snapshot;
+ }
+ String currentStatus;
+ if (snapshot.getUsedPercent().compareTo(BigDecimal.valueOf(target.getAlarmUsagePercent())) >= 0) {
+ currentStatus = DiskMonitorConstant.STATUS_ALARM;
+ } else if (snapshot.getUsedPercent().compareTo(BigDecimal.valueOf(target.getWarningUsagePercent())) >= 0) {
+ currentStatus = DiskMonitorConstant.STATUS_WARNING;
+ } else {
+ currentStatus = DiskMonitorConstant.STATUS_NORMAL;
+ }
+ return new ScanSnapshot(true, snapshot.getTotalBytes(), snapshot.getUsedBytes(), snapshot.getFreeBytes(),
+ snapshot.getUsedPercent(), snapshot.getScanTime(), currentStatus, snapshot.getMessage());
+ }
+
+ private List resolveExecutionTargets(String driveLetter) {
+ if (StrUtil.isNotBlank(driveLetter)) {
+ DiskMonitorTarget target = diskMonitorPolicyService.getTargetByDriveLetter(driveLetter);
+ return target == null ? Collections.emptyList() : Collections.singletonList(target);
+ }
+ return diskMonitorPolicyService.listEnabledTargets();
+ }
+
+ private void logNoExecutionTargets(String jobSource, String driveLetter) {
+ if (StrUtil.isNotBlank(driveLetter)) {
+ log.warn("磁盘监控执行跳过,未找到可执行的盘符配置,jobSource={}, driveLetter={}", jobSource, driveLetter);
+ return;
+ }
+ log.warn("磁盘监控执行跳过,暂无启用的磁盘监控盘符配置,jobSource={}", jobSource);
+ }
+
+ private void validateJobSource(String jobSource) {
+ if (!DiskMonitorConstant.JOB_SOURCE_APP_START.equals(jobSource)
+ && !DiskMonitorConstant.JOB_SOURCE_DAILY_SCHEDULE.equals(jobSource)
+ && !DiskMonitorConstant.JOB_SOURCE_MANUAL.equals(jobSource)) {
+ throw new BusinessException(DiskMonitorResponseEnum.JOB_SOURCE_INVALID);
+ }
+ }
+
+ private boolean isEnabled(Integer value) {
+ return value != null && value == 1;
+ }
+
+ private String resolvePreviousStatus(String previousStatus) {
+ return StrUtil.isBlank(previousStatus) ? DiskMonitorConstant.STATUS_UNKNOWN : previousStatus;
+ }
+
+ private String formatDateTime(LocalDateTime dateTime) {
+ return dateTime == null ? null : dateTime.format(DATE_TIME_FORMATTER);
+ }
+
+ private String generateJobNo() {
+ return "DM" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
+ }
+
+ private DiskMonitorVO.JobListVO toJobListVO(DiskMonitorJob job) {
+ DiskMonitorVO.JobListVO jobListVO = new DiskMonitorVO.JobListVO();
+ jobListVO.setJobId(job.getId());
+ jobListVO.setJobNo(job.getJobNo());
+ jobListVO.setJobSource(job.getJobSource());
+ jobListVO.setStartedAt(formatDateTime(job.getStartedAt()));
+ jobListVO.setFinishedAt(formatDateTime(job.getFinishedAt()));
+ jobListVO.setJobStatus(job.getJobStatus());
+ jobListVO.setTargetCount(job.getTargetCount());
+ jobListVO.setWarningCount(job.getWarningCount());
+ jobListVO.setAlarmCount(job.getAlarmCount());
+ jobListVO.setMessage(job.getMessage());
+ return jobListVO;
+ }
+
+ private DiskMonitorVO.JobInfoVO toJobInfoVO(DiskMonitorJob job) {
+ DiskMonitorVO.JobInfoVO jobInfoVO = new DiskMonitorVO.JobInfoVO();
+ jobInfoVO.setId(job.getId());
+ jobInfoVO.setJobNo(job.getJobNo());
+ jobInfoVO.setJobSource(job.getJobSource());
+ jobInfoVO.setStartedAt(formatDateTime(job.getStartedAt()));
+ jobInfoVO.setFinishedAt(formatDateTime(job.getFinishedAt()));
+ jobInfoVO.setJobStatus(job.getJobStatus());
+ jobInfoVO.setTargetCount(job.getTargetCount());
+ jobInfoVO.setSuccessCount(job.getSuccessCount());
+ jobInfoVO.setWarningCount(job.getWarningCount());
+ jobInfoVO.setAlarmCount(job.getAlarmCount());
+ jobInfoVO.setMessage(job.getMessage());
+ return jobInfoVO;
+ }
+
+ private DiskMonitorVO.ResultVO toResultVO(DiskMonitorResult result) {
+ DiskMonitorVO.ResultVO resultVO = new DiskMonitorVO.ResultVO();
+ resultVO.setResultId(result.getId());
+ resultVO.setTargetId(result.getTargetId());
+ resultVO.setDriveLetter(result.getDriveLetter());
+ resultVO.setTotalBytes(result.getTotalBytes());
+ resultVO.setUsedBytes(result.getUsedBytes());
+ resultVO.setFreeBytes(result.getFreeBytes());
+ resultVO.setUsedPercent(result.getUsedPercent());
+ resultVO.setCurrentStatus(result.getCurrentStatus());
+ resultVO.setPreviousStatus(result.getPreviousStatus());
+ resultVO.setStatusChanged(Objects.equals(result.getStatusChanged(), 1));
+ resultVO.setShouldNotify(Objects.equals(result.getShouldNotify(), 1));
+ resultVO.setNotifyReason(result.getNotifyReason());
+ resultVO.setScanTime(formatDateTime(result.getScanTime()));
+ resultVO.setMessage(result.getMessage());
+ return resultVO;
+ }
+
+ private DiskMonitorVO.NotifyLogVO toNotifyLogVO(DiskMonitorNotifyLog notifyLog) {
+ DiskMonitorVO.NotifyLogVO notifyLogVO = new DiskMonitorVO.NotifyLogVO();
+ notifyLogVO.setId(notifyLog.getId());
+ notifyLogVO.setResultId(notifyLog.getResultId());
+ notifyLogVO.setDriveLetter(notifyLog.getDriveLetter());
+ notifyLogVO.setNotifyLevel(notifyLog.getNotifyLevel());
+ notifyLogVO.setChannelType(notifyLog.getChannelType());
+ notifyLogVO.setChannelTarget(notifyLog.getChannelTarget());
+ notifyLogVO.setSendStatus(notifyLog.getSendStatus());
+ notifyLogVO.setResponseMessage(notifyLog.getResponseMessage());
+ notifyLogVO.setSentAt(formatDateTime(notifyLog.getSentAt()));
+ return notifyLogVO;
+ }
+
+ private static class NotifyDecision {
+ private final boolean shouldNotify;
+ private final String notifyReason;
+ private final String notifyLevel;
+
+ private NotifyDecision(boolean shouldNotify, String notifyReason, String notifyLevel) {
+ this.shouldNotify = shouldNotify;
+ this.notifyReason = notifyReason;
+ this.notifyLevel = notifyLevel;
+ }
+
+ public boolean isShouldNotify() {
+ return shouldNotify;
+ }
+
+ public String getNotifyReason() {
+ return notifyReason;
+ }
+
+ public String getNotifyLevel() {
+ return notifyLevel;
+ }
+ }
+
+ private static class ScanSnapshot {
+ private final boolean success;
+ private final Long totalBytes;
+ private final Long usedBytes;
+ private final Long freeBytes;
+ private final BigDecimal usedPercent;
+ private final LocalDateTime scanTime;
+ private final String currentStatus;
+ private final String message;
+
+ private ScanSnapshot(boolean success, Long totalBytes, Long usedBytes, Long freeBytes,
+ BigDecimal usedPercent, LocalDateTime scanTime, String currentStatus, String message) {
+ this.success = success;
+ this.totalBytes = totalBytes;
+ this.usedBytes = usedBytes;
+ this.freeBytes = freeBytes;
+ this.usedPercent = usedPercent == null ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : usedPercent;
+ this.scanTime = scanTime == null ? LocalDateTime.now() : scanTime;
+ this.currentStatus = StrUtil.blankToDefault(currentStatus, DiskMonitorConstant.STATUS_UNKNOWN);
+ this.message = message;
+ }
+
+ public static ScanSnapshot failed(String message) {
+ return new ScanSnapshot(false, 0L, 0L, 0L, BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP),
+ LocalDateTime.now(), DiskMonitorConstant.STATUS_UNKNOWN, message);
+ }
+
+ public boolean isSuccess() {
+ return success;
+ }
+
+ public Long getTotalBytes() {
+ return totalBytes;
+ }
+
+ public Long getUsedBytes() {
+ return usedBytes;
+ }
+
+ public Long getFreeBytes() {
+ return freeBytes;
+ }
+
+ public BigDecimal getUsedPercent() {
+ return usedPercent;
+ }
+
+ public LocalDateTime getScanTime() {
+ return scanTime;
+ }
+
+ public String getCurrentStatus() {
+ if (!success || totalBytes == null || totalBytes <= 0) {
+ return DiskMonitorConstant.STATUS_UNKNOWN;
+ }
+ return currentStatus == null ? DiskMonitorConstant.STATUS_NORMAL : currentStatus;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+ }
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/impl/DiskMonitorNotifyServiceImpl.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/impl/DiskMonitorNotifyServiceImpl.java
new file mode 100644
index 0000000..77bef26
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/impl/DiskMonitorNotifyServiceImpl.java
@@ -0,0 +1,34 @@
+package com.njcn.gather.systemmonitor.disk.service.impl;
+
+import com.njcn.common.pojo.exception.BusinessException;
+import com.njcn.gather.systemmonitor.disk.enums.DiskMonitorResponseEnum;
+import com.njcn.gather.systemmonitor.disk.pojo.param.DiskMonitorParam;
+import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorTarget;
+import com.njcn.gather.systemmonitor.disk.service.IDiskMonitorJobService;
+import com.njcn.gather.systemmonitor.disk.service.IDiskMonitorNotifyService;
+import com.njcn.gather.systemmonitor.disk.service.IDiskMonitorPolicyService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * 磁盘监控通知服务实现。
+ */
+@Service
+@RequiredArgsConstructor
+public class DiskMonitorNotifyServiceImpl implements IDiskMonitorNotifyService {
+
+ private final IDiskMonitorPolicyService diskMonitorPolicyService;
+ private final IDiskMonitorJobService diskMonitorJobService;
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public boolean testNotify(DiskMonitorParam.NotifyTestParam param) {
+ DiskMonitorTarget target = diskMonitorPolicyService.getTargetByDriveLetter(param.getDriveLetter());
+ if (target == null) {
+ throw new BusinessException(DiskMonitorResponseEnum.NOTIFY_TARGET_NOT_FOUND);
+ }
+ diskMonitorJobService.executeNotifyTest(target.getDriveLetter());
+ return true;
+ }
+}
diff --git a/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/impl/DiskMonitorPolicyServiceImpl.java b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/impl/DiskMonitorPolicyServiceImpl.java
new file mode 100644
index 0000000..489067e
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/java/com/njcn/gather/systemmonitor/disk/service/impl/DiskMonitorPolicyServiceImpl.java
@@ -0,0 +1,341 @@
+package com.njcn.gather.systemmonitor.disk.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+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.systemmonitor.disk.constant.DiskMonitorConstant;
+import com.njcn.gather.systemmonitor.disk.enums.DiskMonitorResponseEnum;
+import com.njcn.gather.systemmonitor.disk.event.DiskMonitorPolicyChangedEvent;
+import com.njcn.gather.systemmonitor.disk.mapper.DiskMonitorPolicyMapper;
+import com.njcn.gather.systemmonitor.disk.mapper.DiskMonitorTargetMapper;
+import com.njcn.gather.systemmonitor.disk.pojo.dto.DiskMonitorNotifyHttpItem;
+import com.njcn.gather.systemmonitor.disk.pojo.dto.DiskMonitorNotifyPathItem;
+import com.njcn.gather.systemmonitor.disk.pojo.param.DiskMonitorParam;
+import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorPolicy;
+import com.njcn.gather.systemmonitor.disk.pojo.po.DiskMonitorTarget;
+import com.njcn.gather.systemmonitor.disk.pojo.vo.DiskMonitorVO;
+import com.njcn.gather.systemmonitor.disk.service.IDiskMonitorPolicyService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.net.URL;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 磁盘监控配置服务实现。
+ */
+@Service
+@RequiredArgsConstructor
+public class DiskMonitorPolicyServiceImpl implements IDiskMonitorPolicyService {
+
+ private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+ private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
+ private static final TypeReference> PATH_LIST_TYPE = new TypeReference>() {
+ };
+ private static final TypeReference> HTTP_LIST_TYPE = new TypeReference>() {
+ };
+
+ private final DiskMonitorPolicyMapper policyMapper;
+ private final DiskMonitorTargetMapper targetMapper;
+ private final ObjectMapper objectMapper;
+ private final ApplicationEventPublisher applicationEventPublisher;
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public DiskMonitorVO.PolicyDetailVO getPolicyDetail() {
+ DiskMonitorPolicy policy = getOrCreatePolicy();
+ DiskMonitorVO.PolicyDetailVO detailVO = new DiskMonitorVO.PolicyDetailVO();
+ detailVO.setPolicy(toPolicyVO(policy));
+ detailVO.setTargets(listTargetsByPolicyId(policy.getId()).stream().map(this::toTargetVO).collect(Collectors.toList()));
+ return detailVO;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public boolean savePolicy(DiskMonitorParam.PolicySaveParam param) {
+ validateSaveParam(param);
+ DiskMonitorPolicy currentPolicy = getOrCreatePolicy();
+ DiskMonitorParam.PolicyParam policyParam = param.getPolicy();
+
+ currentPolicy.setPolicyName(policyParam.getPolicyName().trim());
+ currentPolicy.setMonitorEnabled(boolToInt(policyParam.getMonitorEnabled(), true));
+ currentPolicy.setRunOnAppStart(boolToInt(policyParam.getRunOnAppStart(), true));
+ currentPolicy.setDailyRunTime(parseTime(policyParam.getDailyRunTime()));
+ currentPolicy.setWarningNotifyMode(resolveWarningNotifyMode(policyParam.getWarningNotifyMode()));
+ currentPolicy.setAlarmNotifyMode(resolveAlarmNotifyMode(policyParam.getAlarmNotifyMode()));
+ currentPolicy.setRemark(policyParam.getRemark());
+ if (policyMapper.updateById(currentPolicy) <= 0) {
+ throw new BusinessException(DiskMonitorResponseEnum.POLICY_SAVE_FAILED);
+ }
+
+ List existingTargets = listTargetsByPolicyId(currentPolicy.getId());
+ Map existingById = existingTargets.stream()
+ .collect(Collectors.toMap(DiskMonitorTarget::getId, item -> item, (left, right) -> left, LinkedHashMap::new));
+ Map existingByDrive = existingTargets.stream()
+ .collect(Collectors.toMap(item -> normalizeDriveLetter(item.getDriveLetter()), item -> item, (left, right) -> left, LinkedHashMap::new));
+
+ List keepIds = new ArrayList<>();
+ for (DiskMonitorParam.TargetParam targetParam : param.getTargets()) {
+ String driveLetter = normalizeDriveLetter(targetParam.getDriveLetter());
+ DiskMonitorTarget target = targetParam.getId() == null ? null : existingById.get(targetParam.getId());
+ if (target == null) {
+ target = existingByDrive.get(driveLetter);
+ }
+ if (target == null) {
+ target = new DiskMonitorTarget();
+ target.setPolicyId(currentPolicy.getId());
+ target.setDriveLetter(driveLetter);
+ target.setLastStatus(DiskMonitorConstant.STATUS_UNKNOWN);
+ }
+ fillTarget(target, targetParam, driveLetter);
+ if (target.getId() == null) {
+ targetMapper.insert(target);
+ } else {
+ targetMapper.updateById(target);
+ }
+ keepIds.add(target.getId());
+ }
+
+ List deleteIds = existingTargets.stream()
+ .map(DiskMonitorTarget::getId)
+ .filter(id -> !keepIds.contains(id))
+ .collect(Collectors.toList());
+ if (!deleteIds.isEmpty()) {
+ targetMapper.deleteBatchIds(deleteIds);
+ }
+
+ applicationEventPublisher.publishEvent(new DiskMonitorPolicyChangedEvent());
+ return true;
+ }
+
+ @Override
+ public DiskMonitorPolicy getCurrentPolicy() {
+ return policyMapper.selectOne(new QueryWrapper().orderByAsc("id").last("LIMIT 1"));
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public DiskMonitorPolicy getOrCreatePolicy() {
+ DiskMonitorPolicy policy = getCurrentPolicy();
+ if (policy != null) {
+ return policy;
+ }
+ policy = new DiskMonitorPolicy();
+ policy.setPolicyName(DiskMonitorConstant.DEFAULT_POLICY_NAME);
+ policy.setMonitorEnabled(1);
+ policy.setRunOnAppStart(1);
+ policy.setDailyRunTime(parseTime(DiskMonitorConstant.DEFAULT_DAILY_RUN_TIME));
+ policy.setWarningNotifyMode(DiskMonitorConstant.WARNING_NOTIFY_MODE_STATUS_CHANGE);
+ policy.setAlarmNotifyMode(DiskMonitorConstant.ALARM_NOTIFY_MODE_EVERY_TIME);
+ policyMapper.insert(policy);
+ return policy;
+ }
+
+ @Override
+ public List listEnabledTargets() {
+ return targetMapper.selectList(new QueryWrapper()
+ .eq("monitor_enabled", 1)
+ .orderByAsc("drive_letter", "id"));
+ }
+
+ @Override
+ public DiskMonitorTarget getTargetByDriveLetter(String driveLetter) {
+ return targetMapper.selectOne(new QueryWrapper()
+ .eq("drive_letter", normalizeDriveLetter(driveLetter))
+ .last("LIMIT 1"));
+ }
+
+ private List listTargetsByPolicyId(Long policyId) {
+ return targetMapper.selectList(new QueryWrapper()
+ .eq("policy_id", policyId)
+ .orderByAsc("drive_letter", "id"));
+ }
+
+ private void fillTarget(DiskMonitorTarget target, DiskMonitorParam.TargetParam targetParam, String driveLetter) {
+ target.setDriveLetter(driveLetter);
+ target.setMonitorEnabled(boolToInt(targetParam.getMonitorEnabled(), true));
+ target.setWarningUsagePercent(targetParam.getWarningUsagePercent());
+ target.setAlarmUsagePercent(targetParam.getAlarmUsagePercent());
+ target.setNotifyPathEnabled(boolToInt(targetParam.getNotifyPathEnabled(), false));
+ target.setNotifyPathListJson(writeValueAsJson(targetParam.getNotifyPathList()));
+ target.setNotifyHttpEnabled(boolToInt(targetParam.getNotifyHttpEnabled(), false));
+ target.setNotifyHttpListJson(writeValueAsJson(targetParam.getNotifyHttpList()));
+ target.setRemark(targetParam.getRemark());
+ }
+
+ private void validateSaveParam(DiskMonitorParam.PolicySaveParam param) {
+ DiskMonitorParam.PolicyParam policyParam = param.getPolicy();
+ parseTime(policyParam.getDailyRunTime());
+ resolveWarningNotifyMode(policyParam.getWarningNotifyMode());
+ resolveAlarmNotifyMode(policyParam.getAlarmNotifyMode());
+
+ Map driveMap = new LinkedHashMap<>();
+ for (DiskMonitorParam.TargetParam targetParam : param.getTargets()) {
+ String driveLetter = normalizeDriveLetter(targetParam.getDriveLetter());
+ if (driveMap.put(driveLetter, Boolean.TRUE) != null) {
+ throw new BusinessException(DiskMonitorResponseEnum.DRIVE_LETTER_REPEAT);
+ }
+ if (targetParam.getAlarmUsagePercent() < targetParam.getWarningUsagePercent()) {
+ throw new BusinessException(DiskMonitorResponseEnum.USAGE_PERCENT_INVALID);
+ }
+ if (Boolean.TRUE.equals(targetParam.getNotifyPathEnabled())
+ && (targetParam.getNotifyPathList() == null || targetParam.getNotifyPathList().isEmpty())) {
+ throw new BusinessException(DiskMonitorResponseEnum.NOTIFY_PATH_EMPTY);
+ }
+ if (Boolean.TRUE.equals(targetParam.getNotifyHttpEnabled())
+ && (targetParam.getNotifyHttpList() == null || targetParam.getNotifyHttpList().isEmpty())) {
+ throw new BusinessException(DiskMonitorResponseEnum.NOTIFY_HTTP_EMPTY);
+ }
+ if (targetParam.getNotifyPathList() != null) {
+ for (DiskMonitorNotifyPathItem item : targetParam.getNotifyPathList()) {
+ if (StrUtil.isBlank(item.getPath())) {
+ throw new BusinessException(DiskMonitorResponseEnum.NOTIFY_PATH_VALUE_EMPTY);
+ }
+ }
+ }
+ if (targetParam.getNotifyHttpList() != null) {
+ for (DiskMonitorNotifyHttpItem item : targetParam.getNotifyHttpList()) {
+ validateHttpUrl(item.getUrl());
+ }
+ }
+ }
+ }
+
+ private String resolveWarningNotifyMode(String mode) {
+ String value = StrUtil.blankToDefault(mode, DiskMonitorConstant.WARNING_NOTIFY_MODE_STATUS_CHANGE).trim().toUpperCase(Locale.ROOT);
+ if (!DiskMonitorConstant.WARNING_NOTIFY_MODE_STATUS_CHANGE.equals(value)) {
+ throw new BusinessException(DiskMonitorResponseEnum.POLICY_MODE_INVALID);
+ }
+ return value;
+ }
+
+ private String resolveAlarmNotifyMode(String mode) {
+ String value = StrUtil.blankToDefault(mode, DiskMonitorConstant.ALARM_NOTIFY_MODE_EVERY_TIME).trim().toUpperCase(Locale.ROOT);
+ if (!DiskMonitorConstant.ALARM_NOTIFY_MODE_EVERY_TIME.equals(value)) {
+ throw new BusinessException(DiskMonitorResponseEnum.POLICY_MODE_INVALID);
+ }
+ return value;
+ }
+
+ private void validateHttpUrl(String url) {
+ try {
+ URL httpUrl = new URL(url);
+ httpUrl.toURI();
+ String protocol = httpUrl.getProtocol();
+ if (!"http".equalsIgnoreCase(protocol) && !"https".equalsIgnoreCase(protocol)) {
+ throw new BusinessException(DiskMonitorResponseEnum.NOTIFY_HTTP_URL_INVALID);
+ }
+ } catch (Exception exception) {
+ throw new BusinessException(DiskMonitorResponseEnum.NOTIFY_HTTP_URL_INVALID);
+ }
+ }
+
+ private String normalizeDriveLetter(String driveLetter) {
+ if (StrUtil.isBlank(driveLetter)) {
+ throw new BusinessException(DiskMonitorResponseEnum.DRIVE_LETTER_FORMAT_ERROR);
+ }
+ String normalized = driveLetter.trim().toUpperCase(Locale.ROOT);
+ if (!normalized.matches("^[A-Z]:$")) {
+ throw new BusinessException(DiskMonitorResponseEnum.DRIVE_LETTER_FORMAT_ERROR);
+ }
+ return normalized;
+ }
+
+ private LocalTime parseTime(String time) {
+ try {
+ return LocalTime.parse(time, TIME_FORMATTER);
+ } catch (DateTimeParseException exception) {
+ throw new BusinessException(DiskMonitorResponseEnum.DAILY_RUN_TIME_FORMAT_ERROR);
+ }
+ }
+
+ private int boolToInt(Boolean value, boolean defaultValue) {
+ return Boolean.TRUE.equals(value == null ? defaultValue : value) ? 1 : 0;
+ }
+
+ private boolean isEnabled(Integer value) {
+ return value != null && value == 1;
+ }
+
+ private String writeValueAsJson(Object value) {
+ if (value == null) {
+ return null;
+ }
+ try {
+ return objectMapper.writeValueAsString(value);
+ } catch (JsonProcessingException exception) {
+ throw new BusinessException(CommonResponseEnum.JSON_CONVERT_EXCEPTION, exception.getMessage());
+ }
+ }
+
+ private List parsePathItems(String json) {
+ return readJsonList(json, PATH_LIST_TYPE);
+ }
+
+ private List parseHttpItems(String json) {
+ return readJsonList(json, HTTP_LIST_TYPE);
+ }
+
+ private List readJsonList(String json, TypeReference> typeReference) {
+ if (StrUtil.isBlank(json)) {
+ return new ArrayList<>();
+ }
+ try {
+ List result = objectMapper.readValue(json, typeReference);
+ return result == null ? new ArrayList<>() : result;
+ } catch (Exception exception) {
+ throw new BusinessException(CommonResponseEnum.JSON_CONVERT_EXCEPTION, exception.getMessage());
+ }
+ }
+
+ private DiskMonitorVO.PolicyVO toPolicyVO(DiskMonitorPolicy policy) {
+ DiskMonitorVO.PolicyVO policyVO = new DiskMonitorVO.PolicyVO();
+ policyVO.setId(policy.getId());
+ policyVO.setPolicyName(policy.getPolicyName());
+ policyVO.setMonitorEnabled(isEnabled(policy.getMonitorEnabled()));
+ policyVO.setRunOnAppStart(isEnabled(policy.getRunOnAppStart()));
+ policyVO.setDailyRunTime(policy.getDailyRunTime() == null ? null : policy.getDailyRunTime().format(TIME_FORMATTER));
+ policyVO.setWarningNotifyMode(policy.getWarningNotifyMode());
+ policyVO.setAlarmNotifyMode(policy.getAlarmNotifyMode());
+ policyVO.setLastJobId(policy.getLastJobId());
+ policyVO.setRemark(policy.getRemark());
+ return policyVO;
+ }
+
+ private DiskMonitorVO.TargetVO toTargetVO(DiskMonitorTarget target) {
+ DiskMonitorVO.TargetVO targetVO = new DiskMonitorVO.TargetVO();
+ targetVO.setId(target.getId());
+ targetVO.setDriveLetter(target.getDriveLetter());
+ targetVO.setMonitorEnabled(isEnabled(target.getMonitorEnabled()));
+ targetVO.setWarningUsagePercent(target.getWarningUsagePercent());
+ targetVO.setAlarmUsagePercent(target.getAlarmUsagePercent());
+ targetVO.setNotifyPathEnabled(isEnabled(target.getNotifyPathEnabled()));
+ targetVO.setNotifyPathList(parsePathItems(target.getNotifyPathListJson()));
+ targetVO.setNotifyHttpEnabled(isEnabled(target.getNotifyHttpEnabled()));
+ targetVO.setNotifyHttpList(parseHttpItems(target.getNotifyHttpListJson()));
+ targetVO.setLastStatus(target.getLastStatus());
+ targetVO.setLastScanTime(formatDateTime(target.getLastScanTime()));
+ targetVO.setLastUsedPercent(target.getLastUsedPercent());
+ targetVO.setRemark(target.getRemark());
+ return targetVO;
+ }
+
+ private String formatDateTime(LocalDateTime dateTime) {
+ return dateTime == null ? null : dateTime.format(DATE_TIME_FORMATTER);
+ }
+}
diff --git a/systemmonitor/disk-monitor/src/main/resources/sql/disk-monitor/disk-monitor-init.sql b/systemmonitor/disk-monitor/src/main/resources/sql/disk-monitor/disk-monitor-init.sql
new file mode 100644
index 0000000..13c0f55
--- /dev/null
+++ b/systemmonitor/disk-monitor/src/main/resources/sql/disk-monitor/disk-monitor-init.sql
@@ -0,0 +1,104 @@
+CREATE TABLE IF NOT EXISTS `disk_monitor_policy` (
+ `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
+ `policy_name` VARCHAR(100) NOT NULL DEFAULT '默认磁盘监控策略' COMMENT '策略名称',
+ `monitor_enabled` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否启用监控:0否 1是',
+ `run_on_app_start` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '应用启动后是否执行一次:0否 1是',
+ `daily_run_time` TIME NOT NULL COMMENT '每日统一执行时间',
+ `warning_notify_mode` VARCHAR(32) NOT NULL DEFAULT 'STATUS_CHANGE' COMMENT '预警通知模式',
+ `alarm_notify_mode` VARCHAR(32) NOT NULL DEFAULT 'EVERY_TIME' COMMENT '告警通知模式',
+ `last_job_id` BIGINT NULL COMMENT '最近一次任务ID',
+ `remark` VARCHAR(500) NULL COMMENT '备注',
+ `created_by` VARCHAR(64) NULL COMMENT '创建人',
+ `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updated_by` VARCHAR(64) NULL COMMENT '更新人',
+ `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='磁盘监控全局策略表';
+
+CREATE TABLE IF NOT EXISTS `disk_monitor_target` (
+ `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
+ `policy_id` BIGINT NOT NULL COMMENT '策略ID',
+ `drive_letter` VARCHAR(10) NOT NULL COMMENT '盘符,例如 C:',
+ `monitor_enabled` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否启用监控:0否 1是',
+ `warning_usage_percent` TINYINT UNSIGNED NOT NULL COMMENT '预警使用率阈值',
+ `alarm_usage_percent` TINYINT UNSIGNED NOT NULL COMMENT '告警使用率阈值',
+ `notify_path_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否启用路径通知:0否 1是',
+ `notify_path_list_json` JSON NULL COMMENT '路径通知目标列表JSON',
+ `notify_http_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否启用HTTP通知:0否 1是',
+ `notify_http_list_json` JSON NULL COMMENT 'HTTP通知目标列表JSON',
+ `last_status` VARCHAR(32) NOT NULL DEFAULT 'UNKNOWN' COMMENT '最近一次状态',
+ `last_scan_time` DATETIME NULL COMMENT '最近扫描时间',
+ `last_used_percent` DECIMAL(5,2) NULL COMMENT '最近一次使用率',
+ `remark` VARCHAR(500) NULL COMMENT '备注',
+ `created_by` VARCHAR(64) NULL COMMENT '创建人',
+ `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updated_by` VARCHAR(64) NULL COMMENT '更新人',
+ `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_drive_letter` (`drive_letter`),
+ KEY `idx_policy_id` (`policy_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='磁盘监控盘符配置表';
+
+CREATE TABLE IF NOT EXISTS `disk_monitor_job` (
+ `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
+ `job_no` VARCHAR(64) NOT NULL COMMENT '任务编号',
+ `job_source` VARCHAR(32) NOT NULL COMMENT '任务来源',
+ `planned_time` DATETIME NULL COMMENT '计划执行时间',
+ `started_at` DATETIME NOT NULL COMMENT '开始时间',
+ `finished_at` DATETIME NULL COMMENT '结束时间',
+ `job_status` VARCHAR(32) NOT NULL COMMENT '任务状态',
+ `target_count` INT NOT NULL DEFAULT 0 COMMENT '计划扫描盘符数量',
+ `success_count` INT NOT NULL DEFAULT 0 COMMENT '成功扫描数量',
+ `warning_count` INT NOT NULL DEFAULT 0 COMMENT '预警数量',
+ `alarm_count` INT NOT NULL DEFAULT 0 COMMENT '告警数量',
+ `message` VARCHAR(1000) NULL COMMENT '结果说明',
+ `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_job_no` (`job_no`),
+ KEY `idx_job_source` (`job_source`),
+ KEY `idx_started_at` (`started_at`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='磁盘监控任务批次表';
+
+CREATE TABLE IF NOT EXISTS `disk_monitor_result` (
+ `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
+ `job_id` BIGINT NOT NULL COMMENT '任务ID',
+ `target_id` BIGINT NOT NULL COMMENT '盘符配置ID',
+ `drive_letter` VARCHAR(10) NOT NULL COMMENT '盘符',
+ `total_bytes` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '总容量字节数',
+ `used_bytes` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '已使用字节数',
+ `free_bytes` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '剩余字节数',
+ `used_percent` DECIMAL(5,2) NOT NULL DEFAULT 0.00 COMMENT '使用率',
+ `current_status` VARCHAR(32) NOT NULL COMMENT '当前状态',
+ `previous_status` VARCHAR(32) NOT NULL DEFAULT 'UNKNOWN' COMMENT '上一次状态',
+ `status_changed` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '状态是否变化:0否 1是',
+ `should_notify` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '本次是否通知:0否 1是',
+ `notify_reason` VARCHAR(32) NOT NULL DEFAULT 'NO_NOTIFY' COMMENT '通知原因',
+ `scan_time` DATETIME NOT NULL COMMENT '扫描时间',
+ `message` VARCHAR(1000) NULL COMMENT '扫描说明',
+ PRIMARY KEY (`id`),
+ KEY `idx_job_id` (`job_id`),
+ KEY `idx_target_id` (`target_id`),
+ KEY `idx_drive_letter` (`drive_letter`),
+ KEY `idx_scan_time` (`scan_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='磁盘监控结果表';
+
+CREATE TABLE IF NOT EXISTS `disk_monitor_notify_log` (
+ `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
+ `job_id` BIGINT NOT NULL COMMENT '任务ID',
+ `result_id` BIGINT NOT NULL COMMENT '结果ID',
+ `target_id` BIGINT NOT NULL COMMENT '盘符配置ID',
+ `drive_letter` VARCHAR(10) NOT NULL COMMENT '盘符',
+ `notify_level` VARCHAR(32) NOT NULL COMMENT '通知级别',
+ `channel_type` VARCHAR(32) NOT NULL COMMENT '通知通道类型',
+ `channel_target` VARCHAR(1000) NOT NULL COMMENT '通知目标',
+ `notify_title` VARCHAR(255) NOT NULL COMMENT '通知标题',
+ `notify_content` TEXT NOT NULL COMMENT '通知内容',
+ `send_status` VARCHAR(32) NOT NULL COMMENT '发送状态',
+ `response_message` VARCHAR(2000) NULL COMMENT '响应结果或异常信息',
+ `sent_at` DATETIME NOT NULL COMMENT '发送时间',
+ PRIMARY KEY (`id`),
+ KEY `idx_job_id` (`job_id`),
+ KEY `idx_result_id` (`result_id`),
+ KEY `idx_target_id` (`target_id`),
+ KEY `idx_sent_at` (`sent_at`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='磁盘监控通知日志表';
diff --git a/systemmonitor/pom.xml b/systemmonitor/pom.xml
new file mode 100644
index 0000000..6cf262a
--- /dev/null
+++ b/systemmonitor/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+
+ com.njcn.gather
+ CN_Tool
+ 1.0.0
+
+
+ systemmonitor
+ pom
+ systemmonitor
+ System monitor capability aggregator.
+
+
+ disk-monitor
+
+
+
diff --git a/tools/mms-mapping/API-getIcdMmsJson.md b/tools/mms-mapping/API-getIcdMmsJson.md
new file mode 100644
index 0000000..682a0f0
--- /dev/null
+++ b/tools/mms-mapping/API-getIcdMmsJson.md
@@ -0,0 +1,371 @@
+# getIcdMmsJson 标准 API 调试文档
+
+## 1. 文档范围
+
+本文档用于说明 `mms-mapping` 模块统一调试接口 `getIcdMmsJson` 的标准调用方式、请求结构、响应规则和联调注意事项。
+
+本文档内容以当前源码为准,主要对照以下实现:
+
+- `tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/MappingController.java`
+- `tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/MappingRequestConverter.java`
+- `tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/MappingResponseConverter.java`
+- `tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/service/impl/MappingTaskServiceImpl.java`
+- `tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/MappingGenerationService.java`
+- `tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/GenerateMappingFromIcdRequest.java`
+- `tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/MappingTaskResponse.java`
+
+说明:
+
+- 本文档仅描述接口契约和调试方式,不改动业务代码。
+- 本次未执行 `mvn` 编译、打包或真实接口联调。
+- 如文档与运行结果冲突,以源码和实际部署配置为准。
+
+## 2. 接口基本信息
+
+| 项 | 说明 |
+| --- | --- |
+| 接口名称 | `getIcdMmsJson` |
+| 请求方法 | `POST` |
+| 请求路径 | `/api/mms-mapping/get-icd-mms-json` |
+| Content-Type | `multipart/form-data` |
+| 控制器入口 | `MappingController#getIcdMmsJson` |
+| 请求组成 | `icdFile` 文件 Part + `request` JSON Part |
+| 正常业务响应体 | `MappingTaskResponse` |
+
+## 3. 接口职责
+
+该接口是 `mms-mapping` 模块的统一调试入口,串联以下两个阶段:
+
+1. 上传 ICD 文件并完成解析,生成 `icdDocument` 和 `indexCandidates`
+2. 根据 `request.indexSelection` 判断是否继续生成正式 `mappingJson`
+
+接口行为分为三种典型结果:
+
+1. `request.indexSelection` 未传或为空
+返回 `NEED_INDEX_SELECTION`,用于引导前端或调试人员先确认标签与 `lnInst` 的绑定关系。
+2. `request.indexSelection` 已传但校验不通过
+返回 `NEED_INDEX_SELECTION`,同时通过 `problems` 给出不合法原因,要求重新选择。
+3. `request.indexSelection` 校验通过
+返回 `SUCCESS`,输出正式 `mappingJson`,必要时同时落盘并返回 `savedPath`。
+
+补充说明:
+
+- 该接口每次都会重新解析上传的 ICD 文件,因此第二次调试仍然必须重新上传 ICD 文件。
+- 该接口正常进入业务编排后,返回体类型为 `MappingTaskResponse`。
+- 如果异常发生在控制器参数绑定或请求转换阶段,例如文件为空、Part 缺失、JSON Part 解析失败,则由全局异常处理器统一包装为 `HttpResult`,而不是 `MappingTaskResponse`。
+
+## 4. 请求规范
+
+### 4.1 multipart/form-data Part 说明
+
+| Part 名称 | 类型 | 必填 | 说明 |
+| --- | --- | --- | --- |
+| `icdFile` | File | 是 | ICD 文件,不能为空 |
+| `request` | JSON Part | 是 | 生成参数,必须按 `application/json` 发送 |
+
+说明:
+
+- `request` Part 不能省略。即使第一次只想拿候选结果,也必须传一个最小 JSON。
+- `request.indexSelection` 可以省略或传空数组,此时接口只返回候选结果,不生成正式映射。
+
+### 4.2 request JSON 结构
+
+```json
+{
+ "version": "2026-04-22",
+ "author": "debug-user",
+ "saveToDisk": false,
+ "prettyJson": true,
+ "outputDir": "D:/temp/mms-output",
+ "indexSelection": [
+ {
+ "groupKey": "harm",
+ "groupDesc": "谐波数据",
+ "bindings": [
+ {
+ "reportName": "brcbStHarm",
+ "dataSetName": "dsStHarm",
+ "label": "A相",
+ "lnInst": "1"
+ }
+ ]
+ }
+ ]
+}
+```
+
+### 4.3 request 字段说明
+
+| 字段 | 类型 | 必填 | 说明 |
+| --- | --- | --- | --- |
+| `version` | String | 否 | 输出版本号。未传或空白时,后端按当天日期补齐,格式为 `yyyy-MM-dd` |
+| `author` | String | 否 | 作者。未传或空白时,回退到配置项 `icd.mapping.default-author`,默认值为 `system` |
+| `saveToDisk` | boolean | 否 | 是否将生成结果写入磁盘 |
+| `prettyJson` | boolean | 否 | 是否输出格式化 JSON。`true` 为美化 JSON,`false` 为紧凑 JSON |
+| `outputDir` | String | 否 | 输出目录。未传或空白时,先回退到配置项 `icd.mapping.default-output-dir`;如果配置也为空,最终落到当前工作目录 |
+| `indexSelection` | Array | 否 | 标签与 `lnInst` 的最终绑定关系。未传或为空时,只返回候选结果 |
+
+### 4.4 indexSelection 字段说明
+
+| 字段 | 类型 | 必填 | 说明 |
+| --- | --- | --- | --- |
+| `groupKey` | String | 是 | 分组唯一键,必须使用第一次响应里返回的原值 |
+| `groupDesc` | String | 否 | 分组中文描述,便于调试查看 |
+| `bindings` | Array | 是 | 当前业务分组下最终确认的绑定关系列表 |
+
+### 4.5 bindings 字段说明
+
+| 字段 | 类型 | 必填 | 说明 |
+| --- | --- | --- | --- |
+| `reportName` | String | 是 | 绑定发生在哪个报告上,例如 `brcbStHarm` |
+| `dataSetName` | String | 是 | 绑定发生在哪个数据集上,例如 `dsStHarm` |
+| `label` | String | 是 | 业务标签,例如 `A相`、`最大值`、`实时数据` |
+| `lnInst` | String | 是 | 标签最终绑定到的逻辑节点实例值,例如 `1`、`2`、`3` |
+
+## 5. 标准调试流程
+
+### 5.1 第一次调试:只获取候选结果
+
+用途:
+
+- 上传 ICD 文件
+- 获取 `icdDocument`
+- 获取 `indexCandidates`
+- 确认每个业务分组下可选的 `reportName`、`dataSetName` 和 `availableLnInstValues`
+
+调用要求:
+
+- `request` Part 仍然必须传
+- `request.indexSelection` 可以不传,或传空数组
+
+预期结果:
+
+- `status = NEED_INDEX_SELECTION`
+- 响应中返回 `icdDocument`
+- 响应中返回 `indexCandidates`
+
+### 5.2 第二次调试:带索引绑定生成正式结果
+
+用途:
+
+- 根据第一次返回的 `indexCandidates` 组装 `request.indexSelection`
+- 再次上传同一个 ICD 文件
+- 生成正式 `mappingJson`
+
+调用要求:
+
+- 必须继续上传 `icdFile`
+- `groupKey` 必须沿用第一次返回值
+- `reportName`、`dataSetName`、`lnInst` 必须与第一次返回的候选结果匹配
+
+预期结果:
+
+- `status = SUCCESS`
+- 响应中返回 `mappingJson`
+- 当 `saveToDisk = true` 时,响应中额外返回 `savedPath`
+
+### 5.3 第二次调试但绑定不合法
+
+适用场景:
+
+- `groupKey` 与候选结果不匹配
+- `reportName` 或 `dataSetName` 不在候选集中
+- `lnInst` 不在 `availableLnInstValues` 内
+- 绑定关系缺失、不完整或结构错误
+
+预期结果:
+
+- `status = NEED_INDEX_SELECTION`
+- 响应中仍然返回 `icdDocument` 和 `indexCandidates`
+- `problems` 返回具体问题列表,要求重新确认绑定关系
+
+## 6. 响应规范
+
+### 6.1 正常业务响应体
+
+接口正常进入业务编排后,统一返回 `MappingTaskResponse`。该对象使用了 `@JsonInclude(JsonInclude.Include.NON_EMPTY)`,空字段和空集合不会参与序列化。
+
+基础字段说明:
+
+| 字段 | 类型 | 说明 |
+| --- | --- | --- |
+| `status` | Enum | 本次处理状态,可能为 `SUCCESS`、`NEED_INDEX_SELECTION`、`FAILED` |
+| `message` | String | 状态说明或错误提示 |
+| `icdDocument` | Object | 需要重新选择索引时返回的 ICD 解析结果 |
+| `mappingJson` | String | 正式生成成功后的映射 JSON 文本 |
+| `savedPath` | String | 结果已落盘时返回的绝对路径 |
+| `indexCandidates` | Array | 待绑定状态下返回的索引候选分组 |
+| `problems` | Array | 模板校验、候选分析或绑定校验问题 |
+
+字段出现规则:
+
+| 状态 | 必有字段 | 可能出现字段 |
+| --- | --- | --- |
+| `SUCCESS` | `status`、`message`、`mappingJson` | `savedPath`、`problems` |
+| `NEED_INDEX_SELECTION` | `status`、`message`、`icdDocument`、`indexCandidates` | `problems` |
+| `FAILED` | `status`、`message` | `problems` |
+
+### 6.2 NEED_INDEX_SELECTION 响应示例
+
+```json
+{
+ "status": "NEED_INDEX_SELECTION",
+ "message": "索引配置缺失,请根据候选信息完成标签与数字索引的绑定后重新提交",
+ "icdDocument": {
+ "fileName": "demo.icd",
+ "iedName": "IED1",
+ "ldInst": "LD0",
+ "ldPrefix": "LD",
+ "logicalNodes": [
+ {
+ "lnInst": "1"
+ }
+ ]
+ },
+ "indexCandidates": [
+ {
+ "groupKey": "harm",
+ "groupDesc": "谐波数据",
+ "reportCount": 1,
+ "templateLabels": [
+ "A相",
+ "B相",
+ "C相"
+ ],
+ "reports": [
+ {
+ "reportName": "brcbStHarm",
+ "dataSetName": "dsStHarm",
+ "reportDesc": "谐波报告",
+ "availableLnInstValues": [
+ "1",
+ "2",
+ "3"
+ ]
+ }
+ ]
+ }
+ ]
+}
+```
+
+说明:
+
+- `icdDocument` 实际字段可能比示例更多。
+- 如果本次是“索引配置不合法”而不是“索引配置缺失”,通常还会返回 `problems`。
+
+### 6.3 SUCCESS 响应示例
+
+```json
+{
+ "status": "SUCCESS",
+ "message": "映射生成成功",
+ "mappingJson": "{\n \"version\": \"2026-04-22\",\n \"author\": \"debug-user\",\n \"ied\": \"IED1\",\n \"ld\": \"LD\",\n \"instList\": []\n}"
+}
+```
+
+说明:
+
+- `mappingJson` 是字符串字段,字段值本身是一段 JSON 文本。
+- 当 `saveToDisk = true` 时,响应中还会额外返回 `savedPath`。
+
+### 6.4 FAILED 响应示例
+
+```json
+{
+ "status": "FAILED",
+ "message": "映射生成失败:加载 DefaultCfg.txt 失败:默认模板文件不存在:template/DefaultCfg.txt",
+ "problems": [
+ "加载 DefaultCfg.txt 失败:默认模板文件不存在:template/DefaultCfg.txt"
+ ]
+}
+```
+
+说明:
+
+- `FAILED` 主要对应服务编排阶段捕获到的运行异常,例如 ICD 解析、模板加载、映射生成、序列化或落盘失败。
+- 并非所有错误都会进入 `FAILED`。如果异常发生在控制器参数绑定或请求转换阶段,会走全局异常处理器,而不是这里的业务响应结构。
+
+## 7. 全局异常响应说明
+
+以下场景通常不会返回 `MappingTaskResponse`,而是由 `GlobalBusinessExceptionHandler` 统一包装:
+
+- `icdFile` 缺失或为空
+- `request` Part 缺失
+- `request` Part 的 `Content-Type` 不是 `application/json`
+- `multipart/form-data` 结构不合法
+- JSON 反序列化失败或框架参数绑定失败
+
+这类异常最终会包装为统一的 `HttpResult` 响应,具体字段结构以全局公共响应定义为准,本文不展开其完整协议,只强调:
+
+- 不能把这类错误等同理解为 `MappingTaskResponse.status = FAILED`
+- 联调时应先区分“业务响应体”与“全局异常包装”
+
+## 8. 调试示例
+
+### 8.1 curl 示例:第一次调用,只获取候选结果
+
+```powershell
+curl.exe -X POST "http://localhost:8080/api/mms-mapping/get-icd-mms-json" `
+ -H "Accept: application/json" `
+ -F 'icdFile=@D:/data/demo.icd' `
+ -F 'request={"prettyJson":true,"saveToDisk":false};type=application/json'
+```
+
+### 8.2 curl 示例:第二次调用,带索引绑定直接生成 MMS JSON
+
+```powershell
+curl.exe -X POST "http://localhost:8080/api/mms-mapping/get-icd-mms-json" `
+ -H "Accept: application/json" `
+ -F 'icdFile=@D:/data/demo.icd' `
+ -F 'request={"version":"2026-04-22","author":"debug-user","prettyJson":true,"saveToDisk":false,"indexSelection":[{"groupKey":"harm","groupDesc":"谐波数据","bindings":[{"reportName":"brcbStHarm","dataSetName":"dsStHarm","label":"A相","lnInst":"1"},{"reportName":"brcbStHarm","dataSetName":"dsStHarm","label":"B相","lnInst":"2"},{"reportName":"brcbStHarm","dataSetName":"dsStHarm","label":"C相","lnInst":"3"}]}]};type=application/json'
+```
+
+## 9. Postman 调试要点
+
+1. `Body` 选择 `form-data`
+2. `icdFile` 类型选择 `File`
+3. `request` 保持文本输入,但该 Part 的 `Content-Type` 必须显式设置为 `application/json`
+4. 第一次调试不要省略 `request` Part,只是不传 `indexSelection`
+5. 第二次调试时必须继续上传 ICD 文件,并严格按第一次返回的候选结果组装绑定关系
+
+## 10. 常见问题
+
+### 10.1 为什么第一次调试也必须传 `request`
+
+因为控制器方法签名使用的是 `@RequestPart("request") GenerateMappingFromIcdRequest request`,该 Part 本身就是必填参数。第一次调试可以只传最小 JSON,但不能完全省略。
+
+### 10.2 为什么没有传 `indexSelection`,却没有返回 `FAILED`
+
+这是接口设计的正常行为。`indexSelection` 缺失或为空时,业务语义不是“接口执行失败”,而是“还需要前端继续确认索引绑定”,因此返回的是 `NEED_INDEX_SELECTION`。
+
+### 10.3 `saveToDisk=true` 但没有传 `outputDir`,结果会保存到哪里
+
+处理顺序如下:
+
+1. 先读取请求中的 `outputDir`
+2. 如果请求空白,则回退到配置项 `icd.mapping.default-output-dir`
+3. 如果配置项也为空,则最终落到当前工作目录
+
+### 10.4 `version` 不传时会变成什么
+
+后端在正式生成映射文档时,会把空白 `version` 自动补成当天日期,格式为 `yyyy-MM-dd`。
+
+### 10.5 `mappingJson` 为什么是字符串,不是嵌套对象
+
+因为当前响应结构中 `mappingJson` 定义为 `String`,接口返回的是一段已经序列化好的 JSON 文本,而不是再次展开后的对象结构。
+
+### 10.6 什么情况下会返回 `problems`
+
+`problems` 主要用于承载以下问题:
+
+- 默认模板校验问题
+- 索引候选分析问题
+- `indexSelection` 绑定校验问题
+- 服务编排阶段捕获到的异常原因
+
+## 11. 当前边界
+
+- 当前文档仅覆盖 `getIcdMmsJson` 接口,不覆盖 `get-icd` 与 `get-mms-json` 的独立接口文档
+- 当前文档重点描述业务返回体与调试方式,不展开全局 `HttpResult` 的完整协议
+- 示例中的 `icdDocument`、`indexCandidates` 和 `mappingJson` 为结构化示意,实际字段数量与内容以运行结果为准
diff --git a/tools/mms-mapping/README.md b/tools/mms-mapping/README.md
new file mode 100644
index 0000000..eb4db9c
--- /dev/null
+++ b/tools/mms-mapping/README.md
@@ -0,0 +1,221 @@
+# mms-mapping
+
+`mms-mapping` 模块负责解析 ICD 文件并生成 MMS 映射数据。当前统一调试入口为 `getIcdMmsJson`,该接口同时覆盖“先解析 ICD 获取索引候选”和“确认索引后生成正式 MMS JSON”两类场景。
+
+## 1. 接口信息
+
+- 路径:`POST /api/mms-mapping/get-icd-mms-json`
+- Content-Type:`multipart/form-data`
+- 控制器:`MappingController#getIcdMmsJson`
+- 说明:上传 ICD 文件后,后端先解析 ICD,再根据 `request.indexSelection` 决定返回候选结果还是正式映射结果
+
+说明:
+
+- `request.indexSelection` 为空时,接口返回 `NEED_INDEX_SELECTION`,用于指导前端或调试人员完成标签与 `lnInst` 绑定。
+- `request.indexSelection` 有效时,接口返回 `SUCCESS`,直接给出 `mappingJson`,必要时同时落盘。
+- 该接口每次都会重新解析上传的 ICD 文件,因此二次调试时仍需要重新上传同一个 ICD 文件。
+
+## 2. 调试流程
+
+### 2.1 第一次调试
+
+用途:只上传 ICD,先拿到 `icdDocument` 和 `indexCandidates`。
+
+预期结果:
+
+- `status = NEED_INDEX_SELECTION`
+- 返回 `icdDocument`
+- 返回 `indexCandidates`
+
+### 2.2 第二次调试
+
+用途:根据第一次返回的 `indexCandidates` 组装 `request.indexSelection`,再次调用同一个接口生成正式 MMS JSON。
+
+预期结果:
+
+- `status = SUCCESS`
+- 返回 `mappingJson`
+- 当 `saveToDisk=true` 时返回 `savedPath`
+
+## 3. 请求参数
+
+### 3.1 form-data 参数
+
+| 参数名 | 类型 | 必填 | 说明 |
+| --- | --- | --- | --- |
+| `icdFile` | File | 是 | ICD 文件 |
+| `request` | JSON Part | 是 | 生成参数,必须按 `application/json` 发送 |
+
+### 3.2 request JSON 结构
+
+```json
+{
+ "version": "20260421",
+ "author": "debug-user",
+ "saveToDisk": false,
+ "prettyJson": true,
+ "outputDir": "D:/temp/mms-output",
+ "indexSelection": [
+ {
+ "groupKey": "harm",
+ "groupDesc": "谐波数据",
+ "bindings": [
+ {
+ "reportName": "brcbStHarm",
+ "dataSetName": "dsStHarm",
+ "label": "A相",
+ "lnInst": "1"
+ }
+ ]
+ }
+ ]
+}
+```
+
+### 3.3 request 字段说明
+
+| 字段 | 类型 | 必填 | 说明 |
+| --- | --- | --- | --- |
+| `version` | String | 否 | 映射版本号;为空时由后端按当前日期补齐 |
+| `author` | String | 否 | 作者;为空时使用模块默认作者 |
+| `saveToDisk` | boolean | 否 | 是否将生成结果写入磁盘 |
+| `prettyJson` | boolean | 否 | 是否生成格式化 JSON |
+| `outputDir` | String | 否 | 输出目录;仅 `saveToDisk=true` 时生效 |
+| `indexSelection` | Array | 否 | 标签与 `lnInst` 的最终绑定关系;为空时只返回候选结果 |
+
+### 3.4 indexSelection 字段说明
+
+| 字段 | 类型 | 必填 | 说明 |
+| --- | --- | --- | --- |
+| `groupKey` | String | 是 | 分组唯一键,必须使用第一次响应里返回的原值 |
+| `groupDesc` | String | 否 | 分组描述,便于调试查看 |
+| `bindings` | Array | 是 | 当前分组下最终确认的绑定列表 |
+
+### 3.5 bindings 字段说明
+
+| 字段 | 类型 | 必填 | 说明 |
+| --- | --- | --- | --- |
+| `reportName` | String | 是 | 报告名称 |
+| `dataSetName` | String | 是 | 数据集名称 |
+| `label` | String | 是 | 模板标签 |
+| `lnInst` | String | 是 | 实际绑定的逻辑节点实例值 |
+
+## 4. 调试示例
+
+### 4.1 第一次调用,只获取候选结果
+
+```powershell
+curl.exe -X POST "http://localhost:8080/api/mms-mapping/get-icd-mms-json" `
+ -H "Accept: application/json" `
+ -F 'icdFile=@D:/data/demo.icd' `
+ -F 'request={"version":"20260421","author":"debug-user","prettyJson":true,"saveToDisk":false};type=application/json'
+```
+
+### 4.2 第二次调用,带索引绑定直接生成 MMS JSON
+
+```powershell
+curl.exe -X POST "http://localhost:8080/api/mms-mapping/get-icd-mms-json" `
+ -H "Accept: application/json" `
+ -F 'icdFile=@D:/data/demo.icd' `
+ -F 'request={"version":"20260421","author":"debug-user","prettyJson":true,"saveToDisk":false,"indexSelection":[{"groupKey":"harm","groupDesc":"谐波数据","bindings":[{"reportName":"brcbStHarm","dataSetName":"dsStHarm","label":"A相","lnInst":"1"},{"reportName":"brcbStHarm","dataSetName":"dsStHarm","label":"B相","lnInst":"2"},{"reportName":"brcbStHarm","dataSetName":"dsStHarm","label":"C相","lnInst":"3"}]}]};type=application/json'
+```
+
+## 5. 响应说明
+
+接口统一返回 `MappingTaskResponse`。由于响应对象使用了 `@JsonInclude(JsonInclude.Include.NON_EMPTY)`,空字段和空集合默认不会出现在最终 JSON 中。
+
+### 5.1 NEED_INDEX_SELECTION
+
+适用场景:未传 `indexSelection`,或绑定关系不完整、不合法。
+
+```json
+{
+ "status": "NEED_INDEX_SELECTION",
+ "message": "索引配置缺失,请根据候选信息完成标签与数字索引的绑定后重新提交",
+ "icdDocument": {
+ "fileName": "demo.icd",
+ "iedName": "IED1",
+ "ldInst": "LD0",
+ "ldPrefix": "LD",
+ "logicalNodes": [
+ {
+ "lnInst": "1"
+ }
+ ]
+ },
+ "indexCandidates": [
+ {
+ "groupKey": "harm",
+ "groupDesc": "谐波数据",
+ "reportCount": 1,
+ "templateLabels": [
+ "A相",
+ "B相",
+ "C相"
+ ],
+ "reports": [
+ {
+ "reportName": "brcbStHarm",
+ "dataSetName": "dsStHarm",
+ "reportDesc": "谐波报告",
+ "availableLnInstValues": [
+ "1",
+ "2",
+ "3"
+ ]
+ }
+ ]
+ }
+ ]
+}
+```
+
+说明:
+
+- `icdDocument` 实际返回内容会比示例更大,这里只保留关键字段用于说明结构。
+- 如果绑定值非法,还会额外返回 `problems`,提示缺失或不匹配的绑定项。
+
+### 5.2 SUCCESS
+
+适用场景:`indexSelection` 校验通过,映射成功生成。
+
+```json
+{
+ "status": "SUCCESS",
+ "message": "映射生成成功",
+ "mappingJson": "{\n \"ied\": \"IED1\",\n \"ld\": \"LD\",\n \"instList\": []\n}"
+}
+```
+
+说明:
+
+- `mappingJson` 是字符串字段,字段值本身也是一段 JSON 文本。
+- 当 `saveToDisk=true` 时,响应中还会返回 `savedPath`。
+
+### 5.3 FAILED
+
+适用场景:ICD 解析失败、模板校验失败、文件读取失败或其他运行异常。
+
+```json
+{
+ "status": "FAILED",
+ "message": "映射生成失败:ICD 文件不能为空",
+ "problems": [
+ "ICD 文件不能为空"
+ ]
+}
+```
+
+## 6. Postman 调试注意事项
+
+- `Body` 选择 `form-data`。
+- `icdFile` 类型选择 `File`。
+- `request` 类型保持文本,但该 Part 的 `Content-Type` 需要设置为 `application/json`。
+- 第一次调试建议不要传 `indexSelection`,先观察 `indexCandidates` 和 `availableLnInstValues`。
+- 第二次调试时必须继续上传 ICD 文件,并按第一次返回的 `groupKey`、`reportName`、`dataSetName` 和 `availableLnInstValues` 组装绑定关系。
+
+## 7. 当前限制
+
+- 当前仅补充调试文档,未改动 `mms-mapping` 业务代码。
+- 当前未执行 `mvn` 编译、打包或接口联调验证。
+- 示例响应中的 `icdDocument` 和 `mappingJson` 为结构化示意,实际字段数量以运行结果为准。
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/application/MappingTaskAppService.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/application/MappingTaskAppService.java
deleted file mode 100644
index 41ed340..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/application/MappingTaskAppService.java
+++ /dev/null
@@ -1,135 +0,0 @@
-package com.njcn.gather.icd.mapping.application;
-
-import com.njcn.gather.icd.mapping.application.command.GenerateFromIcdCommand;
-import com.njcn.gather.icd.mapping.application.result.GenerateMappingResult;
-import com.njcn.gather.icd.mapping.domain.model.analysis.IndexAnalysis;
-import com.njcn.gather.icd.mapping.domain.model.analysis.ValidationResult;
-import com.njcn.gather.icd.mapping.domain.model.icd.IcdDocument;
-import com.njcn.gather.icd.mapping.domain.model.mapping.MappingDocument;
-import com.njcn.gather.icd.mapping.domain.model.template.DefaultTemplate;
-import com.njcn.gather.icd.mapping.domain.service.DefaultTemplateLoader;
-import com.njcn.gather.icd.mapping.domain.service.IcdParserService;
-import com.njcn.gather.icd.mapping.domain.service.IndexAnalysisService;
-import com.njcn.gather.icd.mapping.domain.service.IndexValidationService;
-import com.njcn.gather.icd.mapping.domain.service.MappingGenerationService;
-import com.njcn.gather.icd.mapping.enums.GenerateStatus;
-import com.njcn.gather.icd.mapping.infrastructure.serializer.MappingDocumentSerializer;
-import com.njcn.gather.icd.mapping.infrastructure.storage.FileStorageService;
-import org.springframework.stereotype.Service;
-
-/**
- * 生成任务应用服务。
- *
- * 完整流程:
- * 1. 解析 ICD;
- * 2. 读取 DefaultCfg.txt;
- * 3. 按业务分组生成候选项;
- * 4. 如果用户未提交绑定关系,返回 NEED_INDEX_SELECTION;
- * 5. 如果提交了绑定关系,先做合法性校验;
- * 6. 校验通过后生成正式 MappingDocument;
- * 7. 序列化并按需落盘。
- */
-@Service
-public class MappingTaskAppService {
-
- private final IcdParserService icdParserService;
- private final DefaultTemplateLoader defaultTemplateLoader;
- private final IndexAnalysisService indexAnalysisService;
- private final IndexValidationService indexValidationService;
- private final MappingGenerationService mappingGenerationService;
- private final MappingDocumentSerializer mappingDocumentSerializer;
- private final FileStorageService fileStorageService;
-
- public MappingTaskAppService(IcdParserService icdParserService,
- DefaultTemplateLoader defaultTemplateLoader,
- IndexAnalysisService indexAnalysisService,
- IndexValidationService indexValidationService,
- MappingGenerationService mappingGenerationService,
- MappingDocumentSerializer mappingDocumentSerializer,
- FileStorageService fileStorageService) {
- this.icdParserService = icdParserService;
- this.defaultTemplateLoader = defaultTemplateLoader;
- this.indexAnalysisService = indexAnalysisService;
- this.indexValidationService = indexValidationService;
- this.mappingGenerationService = mappingGenerationService;
- this.mappingDocumentSerializer = mappingDocumentSerializer;
- this.fileStorageService = fileStorageService;
- }
-
- public GenerateMappingResult generateFromIcd(GenerateFromIcdCommand command) {
- GenerateMappingResult result = new GenerateMappingResult();
- try {
- // 1. 解析 ICD
- IcdDocument icdDocument = icdParserService.parse(command.getFileBytes(), command.getFileName());
- result.setIedName(icdDocument.getIedName());
- result.setLdInst(icdDocument.getLdInst());
-
- // 2. 加载 DefaultCfg.txt
- DefaultTemplate template = defaultTemplateLoader.load();
- result.getProblems().addAll(template.verify());
-
- // 3. 分析索引候选
- IndexAnalysis indexAnalysis = indexAnalysisService.analyze(icdDocument, template);
- result.setIndexAnalysis(indexAnalysis);
- result.getProblems().addAll(indexAnalysis.getProblems());
-
- // 4. 如果没有提交任何绑定关系,则直接返回待匹配项
- if (command.getIndexSelection() == null || command.getIndexSelection().isEmpty()) {
- result.setStatus(GenerateStatus.NEED_INDEX_SELECTION);
- result.setMessage("索引配置缺失或不合法,请根据候选信息完成标签与数字索引的绑定后重新提交");
- return result;
- }
-
- // 5. 校验用户提交的绑定关系
- ValidationResult validationResult = indexValidationService.validate(indexAnalysis, command.getIndexSelection());
- if (!validationResult.isValid()) {
- result.setStatus(GenerateStatus.NEED_INDEX_SELECTION);
- result.setMessage("索引配置缺失或不合法,请根据候选信息完成标签与数字索引的绑定后重新提交");
- result.getProblems().addAll(validationResult.getProblems());
- return result;
- }
-
- // 6. 生成正式映射结构
- MappingDocument mappingDocument = mappingGenerationService.generate(
- icdDocument,
- template,
- indexAnalysis,
- command.getIndexSelection(),
- command.getVersion(),
- command.getAuthor()
- );
- result.setMappingDocument(mappingDocument);
-
- // 7. 序列化输出
- String mappingJson = command.isPrettyJson()
- ? mappingDocumentSerializer.toPrettyJson(mappingDocument)
- : mappingDocumentSerializer.toCompactJson(mappingDocument);
- result.setMappingJson(mappingJson);
-
- if (command.isSaveToDisk()) {
- String fileName = buildOutputFileName(icdDocument, command.isPrettyJson());
- String savedPath = fileStorageService.save(fileName, mappingJson, command.getOutputDir());
- result.setSavedPath(savedPath);
- }
-
- result.setStatus(GenerateStatus.SUCCESS);
- result.setMessage("映射生成成功");
- return result;
- } catch (Exception ex) {
- result.setStatus(GenerateStatus.FAILED);
- result.setMessage("映射生成失败:" + ex.getMessage());
- result.getProblems().add(ex.getMessage());
- return result;
- }
- }
-
- private String buildOutputFileName(IcdDocument icdDocument, boolean prettyJson) {
- String baseName = icdDocument.getIedName() == null ? "mapping" : icdDocument.getIedName();
- // 落盘文件名只保留安全字符,避免 IED 名称携带路径分隔符导致越界写入。
- String safeBaseName = baseName.replaceAll("[\\\\/:*?\"<>|]+", "_").trim();
- if (safeBaseName.isEmpty()) {
- safeBaseName = "mapping";
- }
- return safeBaseName + (prettyJson ? "-mapping-pretty.json" : "-mapping.json");
- }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/application/command/GenerateFromIcdCommand.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/application/command/GenerateFromIcdCommand.java
deleted file mode 100644
index 23d7479..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/application/command/GenerateFromIcdCommand.java
+++ /dev/null
@@ -1,102 +0,0 @@
-package com.njcn.gather.icd.mapping.application.command;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 生成命令对象。
- *
- * 说明:
- * controller 层不要把 MultipartFile 和 request 直接传进领域服务;
- * 统一转成 command,便于应用层做流程编排。
- */
-public class GenerateFromIcdCommand {
-
- /** 原始文件名。 */
- private String fileName;
-
- /** ICD 文件字节数组。 */
- private byte[] fileBytes;
-
- /** 输出版本号。 */
- private String version;
-
- /** 作者。 */
- private String author;
-
- /** 是否保存到磁盘。 */
- private boolean saveToDisk;
-
- /** 是否输出美化 JSON。 */
- private boolean prettyJson;
-
- /** 输出目录。 */
- private String outputDir;
-
- /** 用户上送的索引选择结果。 */
- private List indexSelection = new ArrayList();
-
- public String getFileName() {
- return fileName;
- }
-
- public void setFileName(String fileName) {
- this.fileName = fileName;
- }
-
- public byte[] getFileBytes() {
- return fileBytes;
- }
-
- public void setFileBytes(byte[] fileBytes) {
- this.fileBytes = fileBytes;
- }
-
- public String getVersion() {
- return version;
- }
-
- public void setVersion(String version) {
- this.version = version;
- }
-
- public String getAuthor() {
- return author;
- }
-
- public void setAuthor(String author) {
- this.author = author;
- }
-
- public boolean isSaveToDisk() {
- return saveToDisk;
- }
-
- public void setSaveToDisk(boolean saveToDisk) {
- this.saveToDisk = saveToDisk;
- }
-
- public boolean isPrettyJson() {
- return prettyJson;
- }
-
- public void setPrettyJson(boolean prettyJson) {
- this.prettyJson = prettyJson;
- }
-
- public String getOutputDir() {
- return outputDir;
- }
-
- public void setOutputDir(String outputDir) {
- this.outputDir = outputDir;
- }
-
- public List getIndexSelection() {
- return indexSelection;
- }
-
- public void setIndexSelection(List indexSelection) {
- this.indexSelection = indexSelection;
- }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/application/command/IndexBindingCommand.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/application/command/IndexBindingCommand.java
deleted file mode 100644
index 44a065b..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/application/command/IndexBindingCommand.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.njcn.gather.icd.mapping.application.command;
-
-/**
- * 应用层单条绑定命令。
- */
-public class IndexBindingCommand {
-
- /** 报告名。 */
- private String reportName;
-
- /** 数据集名。 */
- private String dataSetName;
-
- /** 标签。 */
- private String label;
-
- /** 绑定到的 lnInst 数字。 */
- private String lnInst;
-
- public String getReportName() {
- return reportName;
- }
-
- public void setReportName(String reportName) {
- this.reportName = reportName;
- }
-
- public String getDataSetName() {
- return dataSetName;
- }
-
- public void setDataSetName(String dataSetName) {
- this.dataSetName = dataSetName;
- }
-
- public String getLabel() {
- return label;
- }
-
- public void setLabel(String label) {
- this.label = label;
- }
-
- public String getLnInst() {
- return lnInst;
- }
-
- public void setLnInst(String lnInst) {
- this.lnInst = lnInst;
- }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/application/command/IndexSelectionGroupCommand.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/application/command/IndexSelectionGroupCommand.java
deleted file mode 100644
index ad7e239..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/application/command/IndexSelectionGroupCommand.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.njcn.gather.icd.mapping.application.command;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 应用层分组选择命令。
- */
-public class IndexSelectionGroupCommand {
-
- /** 分组唯一键。 */
- private String groupKey;
-
- /** 分组中文描述。 */
- private String groupDesc;
-
- /** 当前分组下的多条绑定关系。 */
- private List bindings = new ArrayList();
-
- public String getGroupKey() {
- return groupKey;
- }
-
- public void setGroupKey(String groupKey) {
- this.groupKey = groupKey;
- }
-
- public String getGroupDesc() {
- return groupDesc;
- }
-
- public void setGroupDesc(String groupDesc) {
- this.groupDesc = groupDesc;
- }
-
- public List getBindings() {
- return bindings;
- }
-
- public void setBindings(List bindings) {
- this.bindings = bindings;
- }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/application/result/GenerateMappingResult.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/application/result/GenerateMappingResult.java
deleted file mode 100644
index 1330121..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/application/result/GenerateMappingResult.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.njcn.gather.icd.mapping.application.result;
-
-import com.njcn.gather.icd.mapping.domain.model.analysis.IndexAnalysis;
-import com.njcn.gather.icd.mapping.domain.model.mapping.MappingDocument;
-import com.njcn.gather.icd.mapping.enums.GenerateStatus;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 应用层返回对象。统一封装成功、需要选择索引、失败三类结果。
- */
-public class GenerateMappingResult {
- private GenerateStatus status;
- private String message;
- private String iedName;
- private String ldInst;
- private IndexAnalysis indexAnalysis;
- private MappingDocument mappingDocument;
- private String mappingJson;
- private String savedPath;
- private List problems = new ArrayList();
-
- public GenerateStatus getStatus() { return status; }
- public void setStatus(GenerateStatus status) { this.status = status; }
- public String getMessage() { return message; }
- public void setMessage(String message) { this.message = message; }
- public String getIedName() { return iedName; }
- public void setIedName(String iedName) { this.iedName = iedName; }
- public String getLdInst() { return ldInst; }
- public void setLdInst(String ldInst) { this.ldInst = ldInst; }
- public IndexAnalysis getIndexAnalysis() { return indexAnalysis; }
- public void setIndexAnalysis(IndexAnalysis indexAnalysis) { this.indexAnalysis = indexAnalysis; }
- public MappingDocument getMappingDocument() { return mappingDocument; }
- public void setMappingDocument(MappingDocument mappingDocument) { this.mappingDocument = mappingDocument; }
- public String getMappingJson() { return mappingJson; }
- public void setMappingJson(String mappingJson) { this.mappingJson = mappingJson; }
- public String getSavedPath() { return savedPath; }
- public void setSavedPath(String savedPath) { this.savedPath = savedPath; }
- public List getProblems() { return problems; }
- public void setProblems(List problems) { this.problems = problems; }
-}
\ No newline at end of file
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/service/DefaultTemplateLoader.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/DefaultTemplateLoader.java
similarity index 85%
rename from tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/service/DefaultTemplateLoader.java
rename to tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/DefaultTemplateLoader.java
index 852ea08..2aea155 100644
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/service/DefaultTemplateLoader.java
+++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/DefaultTemplateLoader.java
@@ -1,9 +1,9 @@
-package com.njcn.gather.icd.mapping.domain.service;
+package com.njcn.gather.icd.mapping.component;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.njcn.gather.icd.mapping.config.MappingModuleConfig;
-import com.njcn.gather.icd.mapping.domain.model.template.DefaultTemplate;
+import com.njcn.gather.icd.mapping.pojo.bo.template.DefaultTemplate;
import com.njcn.gather.icd.mapping.utils.JsonUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
@@ -23,7 +23,10 @@ import java.util.List;
@Service
public class DefaultTemplateLoader {
+ /** 模块配置,提供默认模板路径等运行参数。 */
private final MappingModuleConfig moduleConfig;
+
+ /** 模板反序列化使用的 Jackson 实例。 */
private final ObjectMapper objectMapper;
public DefaultTemplateLoader(MappingModuleConfig moduleConfig) {
@@ -32,6 +35,11 @@ public class DefaultTemplateLoader {
this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
+ /**
+ * 加载并校验默认映射模板。
+ *
+ * 返回值会直接参与索引候选分析和最终 MappingDocument 生成。
+ */
public DefaultTemplate load() {
try {
ClassPathResource resource = new ClassPathResource(moduleConfig.getDefaultTemplatePath());
@@ -55,6 +63,9 @@ public class DefaultTemplateLoader {
}
}
+ /**
+ * 读取 classpath 资源的完整字节内容。
+ */
private byte[] readAllBytes(ClassPathResource resource) throws Exception {
try (InputStream inputStream = resource.getInputStream();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/infrastructure/storage/FileStorageService.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/FileStorageService.java
similarity index 80%
rename from tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/infrastructure/storage/FileStorageService.java
rename to tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/FileStorageService.java
index d76b74a..c6f41e7 100644
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/infrastructure/storage/FileStorageService.java
+++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/FileStorageService.java
@@ -1,4 +1,4 @@
-package com.njcn.gather.icd.mapping.infrastructure.storage;
+package com.njcn.gather.icd.mapping.component;
import org.springframework.stereotype.Component;
@@ -11,8 +11,14 @@ import java.nio.charset.StandardCharsets;
*/
@Component
public class FileStorageService {
+ /**
+ * 将映射 JSON 写入目标目录。
+ *
+ * outputDir 为空时使用当前工作目录,返回最终文件绝对路径。
+ */
public String save(String fileName, String content, String outputDir) {
try {
+ // 输出目录允许由请求覆盖,未传时落到当前工作目录。
File dir = outputDir == null || outputDir.trim().isEmpty() ? new File(".") : new File(outputDir);
if (!dir.exists() && !dir.mkdirs()) {
throw new IllegalStateException("输出目录创建失败:" + dir.getAbsolutePath());
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/service/IcdParserService.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/IcdParserService.java
similarity index 67%
rename from tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/service/IcdParserService.java
rename to tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/IcdParserService.java
index 729fd63..d0f3041 100644
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/service/IcdParserService.java
+++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/IcdParserService.java
@@ -1,6 +1,6 @@
-package com.njcn.gather.icd.mapping.domain.service;
+package com.njcn.gather.icd.mapping.component;
-import com.njcn.gather.icd.mapping.domain.model.icd.IcdDocument;
+import com.njcn.gather.icd.mapping.pojo.bo.icd.IcdDocument;
import com.njcn.gather.icd.mapping.infrastructure.parser.SclParserAdapter;
import org.springframework.stereotype.Service;
@@ -11,12 +11,16 @@ import org.springframework.stereotype.Service;
@Service
public class IcdParserService {
+ /** SCL 底层解析适配器,封装 JAXB generated 模型处理。 */
private final SclParserAdapter parserAdapter;
public IcdParserService(SclParserAdapter parserAdapter) {
this.parserAdapter = parserAdapter;
}
+ /**
+ * 解析 ICD 文件内容为模块内部统一领域模型。
+ */
public IcdDocument parse(byte[] bytes, String fileName) {
return parserAdapter.parse(bytes, fileName);
}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/service/IndexAnalysisService.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/IndexAnalysisService.java
similarity index 78%
rename from tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/service/IndexAnalysisService.java
rename to tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/IndexAnalysisService.java
index 3365211..03f73fa 100644
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/service/IndexAnalysisService.java
+++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/IndexAnalysisService.java
@@ -1,13 +1,13 @@
-package com.njcn.gather.icd.mapping.domain.service;
+package com.njcn.gather.icd.mapping.component;
-import com.njcn.gather.icd.mapping.domain.model.analysis.IndexAnalysis;
-import com.njcn.gather.icd.mapping.domain.model.analysis.IndexCandidate;
-import com.njcn.gather.icd.mapping.domain.model.analysis.IndexCandidateReportItem;
-import com.njcn.gather.icd.mapping.domain.model.icd.DataSetNode;
-import com.njcn.gather.icd.mapping.domain.model.icd.FcdaNode;
-import com.njcn.gather.icd.mapping.domain.model.icd.IcdDocument;
-import com.njcn.gather.icd.mapping.domain.model.icd.ReportControlNode;
-import com.njcn.gather.icd.mapping.domain.model.template.DefaultTemplate;
+import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexAnalysis;
+import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexCandidate;
+import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexCandidateReportItem;
+import com.njcn.gather.icd.mapping.pojo.bo.icd.DataSetNode;
+import com.njcn.gather.icd.mapping.pojo.bo.icd.FcdaNode;
+import com.njcn.gather.icd.mapping.pojo.bo.icd.IcdDocument;
+import com.njcn.gather.icd.mapping.pojo.bo.icd.ReportControlNode;
+import com.njcn.gather.icd.mapping.pojo.bo.template.DefaultTemplate;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
@@ -21,17 +21,15 @@ import java.util.Set;
/**
* 索引候选分析服务。
*
- * 说明:
- * 1. 新版不再按“单个报告”平铺返回,而是按 DefaultCfg.ReportList 的业务配置项聚合。
- * 2. 一个业务配置项下可能包含多个报告,因此这里会计算 reportCount,并返回 reports 子列表。
- * 3. templateLabels 只是模板参考,不要求与 ICD 解析到的 lnInst 数量完全一一对应。
- * 4. 关键修正:
- * 在这里就把 DefaultCfg.ReportList 的 inst / Select / TrgOps 一并带入 IndexCandidate,
- * 后续正式生成阶段直接使用,不再重新查模板。
+ * 按 `DefaultCfg.ReportList` 的业务分组聚合 ICD 中的报告信息,
+ * 同时把后续正式生成阶段需要的模板字段一并带入候选结果。
*/
@Service
public class IndexAnalysisService {
+ /**
+ * 根据 ICD 报告控制块和 DefaultCfg.ReportList 生成索引候选分组。
+ */
public IndexAnalysis analyze(IcdDocument icdDocument, DefaultTemplate template) {
IndexAnalysis analysis = new IndexAnalysis();
if (icdDocument == null) {
@@ -43,7 +41,7 @@ public class IndexAnalysisService {
return analysis;
}
- // 先按模板分组聚合
+ // 先按模板分组聚合候选项,而不是按单个报告平铺返回。
for (DefaultTemplate.ReportCfgItem reportCfg : template.getReportList()) {
List matchedReports = collectMatchedReports(icdDocument, reportCfg);
if (matchedReports.isEmpty()) {
@@ -55,7 +53,7 @@ public class IndexAnalysisService {
candidate.setGroupDesc(reportCfg.getDesc());
candidate.setReportCount(matchedReports.size());
- // 关键:把 DefaultCfg.ReportList 的配置项直接带入候选对象
+ // 把模板中的关键配置直接带入候选对象,避免正式生成阶段再次回查模板。
candidate.setReportInst(reportCfg.getInst());
candidate.setSelect(reportCfg.getSelect());
candidate.setTrgOps(reportCfg.getTrgOps());
@@ -76,7 +74,7 @@ public class IndexAnalysisService {
analysis.getCandidates().add(candidate);
}
- // 再检查是否有 ICD 报告没有被模板覆盖
+ // 检查 ICD 中是否存在未被模板覆盖的报告配置。
if (icdDocument.getReportControls() != null) {
for (ReportControlNode reportControl : icdDocument.getReportControls()) {
if (!isCoveredByTemplate(reportControl, template)) {
@@ -91,6 +89,9 @@ public class IndexAnalysisService {
return analysis;
}
+ /**
+ * 收集命中当前模板报告分组 DataSetList 的报告控制块。
+ */
private List collectMatchedReports(IcdDocument icdDocument, DefaultTemplate.ReportCfgItem reportCfg) {
List result = new ArrayList();
if (icdDocument.getReportControls() == null || reportCfg.getDataSetList() == null) {
@@ -104,6 +105,9 @@ public class IndexAnalysisService {
return result;
}
+ /**
+ * 判断 ICD 中的报告是否已经被模板 ReportList 覆盖。
+ */
private boolean isCoveredByTemplate(ReportControlNode reportControl, DefaultTemplate template) {
if (template == null || template.getReportList() == null) {
return false;
@@ -116,6 +120,9 @@ public class IndexAnalysisService {
return false;
}
+ /**
+ * 从指定 DataSet 的 FCDA 中提取可选 lnInst 数字,并按数字优先排序。
+ */
private List collectLnInstValues(IcdDocument icdDocument, String dataSetName) {
if (icdDocument.getDataSets() == null) {
return Collections.emptyList();
@@ -151,6 +158,9 @@ public class IndexAnalysisService {
return result;
}
+ /**
+ * 构造前端回传使用的稳定分组 key。
+ */
private String buildGroupKey(DefaultTemplate.ReportCfgItem reportCfg) {
String desc = reportCfg.getDesc() == null ? "GROUP" : reportCfg.getDesc();
String firstDataSet = (reportCfg.getDataSetList() == null || reportCfg.getDataSetList().isEmpty())
@@ -158,6 +168,9 @@ public class IndexAnalysisService {
return normalize(desc) + "__" + normalize(firstDataSet);
}
+ /**
+ * 将中文描述和 DataSet 名转换为可比较的 key 片段。
+ */
private String normalize(String value) {
return value.replaceAll("[^0-9A-Za-z\\u4e00-\\u9fa5]+", "_")
.replaceAll("_+", "_")
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/service/IndexValidationService.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/IndexValidationService.java
similarity index 81%
rename from tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/service/IndexValidationService.java
rename to tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/IndexValidationService.java
index eb10544..1284afe 100644
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/service/IndexValidationService.java
+++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/IndexValidationService.java
@@ -1,11 +1,11 @@
-package com.njcn.gather.icd.mapping.domain.service;
+package com.njcn.gather.icd.mapping.component;
-import com.njcn.gather.icd.mapping.application.command.IndexBindingCommand;
-import com.njcn.gather.icd.mapping.application.command.IndexSelectionGroupCommand;
-import com.njcn.gather.icd.mapping.domain.model.analysis.IndexAnalysis;
-import com.njcn.gather.icd.mapping.domain.model.analysis.IndexCandidate;
-import com.njcn.gather.icd.mapping.domain.model.analysis.IndexCandidateReportItem;
-import com.njcn.gather.icd.mapping.domain.model.analysis.ValidationResult;
+import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexAnalysis;
+import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexCandidate;
+import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexCandidateReportItem;
+import com.njcn.gather.icd.mapping.pojo.bo.analysis.ValidationResult;
+import com.njcn.gather.icd.mapping.pojo.dto.IndexBindingCommand;
+import com.njcn.gather.icd.mapping.pojo.dto.IndexSelectionGroupCommand;
import org.springframework.stereotype.Service;
import java.util.List;
@@ -15,13 +15,16 @@ import java.util.List;
*
* 校验原则:
* 1. 只校验用户明确提交的绑定项;
- * 2. 不要求模板里的所有标签都必须配置;
+ * 2. 不强制模板中的所有标签都必须配置;
* 3. label 必须属于当前业务分组的 templateLabels;
* 4. lnInst 必须属于当前报告对应的 ICD 候选数字。
*/
@Service
public class IndexValidationService {
+ /**
+ * 校验前端提交的分组绑定是否能落到当前 ICD 候选范围内。
+ */
public ValidationResult validate(IndexAnalysis analysis, List selections) {
ValidationResult result = new ValidationResult();
@@ -33,7 +36,7 @@ public class IndexValidationService {
if (selections == null || selections.isEmpty()) {
for (IndexCandidate candidate : analysis.getCandidates()) {
result.getProblems().add(
- "报告组【" + candidate.getGroupDesc() + "】未提交绑定关系,请根据 templateLabels 与 reports[*].availableLnInstValues 完成配置"
+ "分组【" + candidate.getGroupDesc() + "】未提交绑定关系,请根据 templateLabels 与 reports[*].availableLnInstValues 完成配置"
);
}
return result;
@@ -62,6 +65,9 @@ public class IndexValidationService {
return result;
}
+ /**
+ * 校验单条 label + report + lnInst 绑定。
+ */
private void validateBinding(IndexCandidate candidate, IndexBindingCommand binding, ValidationResult result) {
if (binding == null) {
result.getProblems().add("存在空的绑定项");
@@ -93,6 +99,9 @@ public class IndexValidationService {
}
}
+ /**
+ * 优先按 groupKey 匹配候选分组,兼容只传 groupDesc 的旧调用。
+ */
private IndexCandidate findCandidate(IndexAnalysis analysis, String groupKey, String groupDesc) {
for (IndexCandidate candidate : analysis.getCandidates()) {
if (same(candidate.getGroupKey(), groupKey)) {
@@ -105,6 +114,9 @@ public class IndexValidationService {
return null;
}
+ /**
+ * 在候选分组内定位具体报告,dataSetName 为空时只按报告名匹配。
+ */
private IndexCandidateReportItem findReport(IndexCandidate candidate, String reportName, String dataSetName) {
if (candidate.getReports() == null) {
return null;
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/infrastructure/serializer/MappingDocumentSerializer.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/MappingDocumentSerializer.java
similarity index 78%
rename from tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/infrastructure/serializer/MappingDocumentSerializer.java
rename to tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/MappingDocumentSerializer.java
index 7ce88bc..a58877d 100644
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/infrastructure/serializer/MappingDocumentSerializer.java
+++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/MappingDocumentSerializer.java
@@ -1,9 +1,9 @@
-package com.njcn.gather.icd.mapping.infrastructure.serializer;
+package com.njcn.gather.icd.mapping.component;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
-import com.njcn.gather.icd.mapping.domain.model.mapping.MappingDocument;
+import com.njcn.gather.icd.mapping.pojo.bo.mapping.MappingDocument;
import org.springframework.stereotype.Component;
/**
@@ -12,7 +12,10 @@ import org.springframework.stereotype.Component;
@Component
public class MappingDocumentSerializer {
+ /** 紧凑 JSON 输出器,用于减少响应体体积。 */
private final ObjectMapper compactMapper;
+
+ /** 格式化 JSON 输出器,用于人工查看和落盘审阅。 */
private final ObjectMapper prettyMapper;
public MappingDocumentSerializer() {
@@ -24,6 +27,9 @@ public class MappingDocumentSerializer {
prettyMapper.enable(SerializationFeature.INDENT_OUTPUT);
}
+ /**
+ * 序列化为紧凑 JSON。
+ */
public String toCompactJson(MappingDocument document) {
try {
return compactMapper.writeValueAsString(document);
@@ -32,6 +38,9 @@ public class MappingDocumentSerializer {
}
}
+ /**
+ * 序列化为格式化 JSON。
+ */
public String toPrettyJson(MappingDocument document) {
try {
return prettyMapper.writeValueAsString(document);
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/service/MappingGenerationService.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/MappingGenerationService.java
similarity index 91%
rename from tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/service/MappingGenerationService.java
rename to tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/MappingGenerationService.java
index 0451a79..4f58967 100644
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/service/MappingGenerationService.java
+++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/MappingGenerationService.java
@@ -1,29 +1,29 @@
-package com.njcn.gather.icd.mapping.domain.service;
+package com.njcn.gather.icd.mapping.component;
-import com.njcn.gather.icd.mapping.application.command.IndexBindingCommand;
-import com.njcn.gather.icd.mapping.application.command.IndexSelectionGroupCommand;
-import com.njcn.gather.icd.mapping.domain.model.analysis.IndexAnalysis;
-import com.njcn.gather.icd.mapping.domain.model.analysis.IndexCandidate;
-import com.njcn.gather.icd.mapping.domain.model.analysis.IndexCandidateReportItem;
-import com.njcn.gather.icd.mapping.domain.model.icd.DataSetNode;
-import com.njcn.gather.icd.mapping.domain.model.icd.DoiElementNode;
-import com.njcn.gather.icd.mapping.domain.model.icd.DoiNode;
-import com.njcn.gather.icd.mapping.domain.model.icd.FcdaNode;
-import com.njcn.gather.icd.mapping.domain.model.icd.IcdDocument;
-import com.njcn.gather.icd.mapping.domain.model.icd.LnNode;
-import com.njcn.gather.icd.mapping.domain.model.icd.ReportControlNode;
-import com.njcn.gather.icd.mapping.domain.model.intermediate.DataSetSelectionState;
-import com.njcn.gather.icd.mapping.domain.model.intermediate.ReportAndDataSetState;
-import com.njcn.gather.icd.mapping.domain.model.intermediate.ReportBindingState;
-import com.njcn.gather.icd.mapping.domain.model.intermediate.ReportGroupState;
-import com.njcn.gather.icd.mapping.domain.model.mapping.DataSetGroupItem;
-import com.njcn.gather.icd.mapping.domain.model.mapping.DoiItem;
-import com.njcn.gather.icd.mapping.domain.model.mapping.InstItem;
-import com.njcn.gather.icd.mapping.domain.model.mapping.MappingDocument;
-import com.njcn.gather.icd.mapping.domain.model.mapping.ReportMapItem;
-import com.njcn.gather.icd.mapping.domain.model.mapping.SdiItem;
-import com.njcn.gather.icd.mapping.domain.model.mapping.TypeItem;
-import com.njcn.gather.icd.mapping.domain.model.template.DefaultTemplate;
+import com.njcn.gather.icd.mapping.pojo.dto.IndexBindingCommand;
+import com.njcn.gather.icd.mapping.pojo.dto.IndexSelectionGroupCommand;
+import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexAnalysis;
+import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexCandidate;
+import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexCandidateReportItem;
+import com.njcn.gather.icd.mapping.pojo.bo.icd.DataSetNode;
+import com.njcn.gather.icd.mapping.pojo.bo.icd.DoiElementNode;
+import com.njcn.gather.icd.mapping.pojo.bo.icd.DoiNode;
+import com.njcn.gather.icd.mapping.pojo.bo.icd.FcdaNode;
+import com.njcn.gather.icd.mapping.pojo.bo.icd.IcdDocument;
+import com.njcn.gather.icd.mapping.pojo.bo.icd.LnNode;
+import com.njcn.gather.icd.mapping.pojo.bo.icd.ReportControlNode;
+import com.njcn.gather.icd.mapping.pojo.bo.state.DataSetSelectionState;
+import com.njcn.gather.icd.mapping.pojo.bo.state.ReportAndDataSetState;
+import com.njcn.gather.icd.mapping.pojo.bo.state.ReportBindingState;
+import com.njcn.gather.icd.mapping.pojo.bo.state.ReportGroupState;
+import com.njcn.gather.icd.mapping.pojo.bo.mapping.DataSetGroupItem;
+import com.njcn.gather.icd.mapping.pojo.bo.mapping.DoiItem;
+import com.njcn.gather.icd.mapping.pojo.bo.mapping.InstItem;
+import com.njcn.gather.icd.mapping.pojo.bo.mapping.MappingDocument;
+import com.njcn.gather.icd.mapping.pojo.bo.mapping.ReportMapItem;
+import com.njcn.gather.icd.mapping.pojo.bo.mapping.SdiItem;
+import com.njcn.gather.icd.mapping.pojo.bo.mapping.TypeItem;
+import com.njcn.gather.icd.mapping.pojo.bo.template.DefaultTemplate;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
@@ -46,6 +46,9 @@ import java.time.format.DateTimeFormatter;
@Service
public class MappingGenerationService {
+ /**
+ * 根据 ICD、默认模板和用户索引绑定生成最终映射文档。
+ */
public MappingDocument generate(IcdDocument icdDocument,
DefaultTemplate template,
IndexAnalysis analysis,
@@ -103,6 +106,7 @@ public class MappingGenerationService {
}
// 2. 生成 DataSetList
+ // DataSetList 按 desc + lnClass 聚合,保持输出顺序稳定。
Map dataSetGroupMap = new LinkedHashMap();
for (DataSetSelectionState selectionState : state.getDataSetSelections()) {
if (selectionState.getLnNodes() == null || selectionState.getLnNodes().isEmpty()) {
@@ -140,6 +144,11 @@ public class MappingGenerationService {
return document;
}
+ /**
+ * 构建正式生成前的中间态。
+ *
+ * 中间态把报告分组、用户绑定和命中的 LN 节点拆开保存,避免生成 JSON 时重复查找。
+ */
private ReportAndDataSetState buildState(IcdDocument icdDocument,
DefaultTemplate template,
IndexAnalysis analysis,
@@ -263,6 +272,9 @@ public class MappingGenerationService {
return result;
}
+ /**
+ * 在 lnClass 分组下查找或创建指定 lnInst 的输出节点。
+ */
private InstItem findOrCreateInst(DataSetGroupItem groupItem, String inst, String desc) {
for (InstItem item : groupItem.getInstList()) {
if (equalsTrim(item.getInst(), inst)) {
@@ -372,6 +384,9 @@ public class MappingGenerationService {
instItem.getDoiList().add(doiItem);
}
+ /**
+ * 判断 queueList 命中的对象是否应按原 C# 规则跳过。
+ */
private boolean shouldSkipQueueItem(boolean queueMode, int icdCount, DefaultTemplate.ObjectCfgItem objectCfg) {
if (!queueMode || objectCfg == null) {
return false;
@@ -418,6 +433,9 @@ public class MappingGenerationService {
return false;
}
+ /**
+ * 根据用户绑定标签查找对应的 DataObjectsList 配置项。
+ */
private List findDataObjectCfgItems(DefaultTemplate template, String label) {
List result = new ArrayList();
if (template == null || template.getDataObjectsList() == null) {
@@ -639,6 +657,11 @@ public class MappingGenerationService {
}
}
+ /**
+ * 解析 DOI 的倍率系数。
+ *
+ * 找不到模板倍率配置时默认返回 1.0。
+ */
private float resolveCoefficient(DoiNode doiNode, DefaultTemplate template) {
Set values = collectAllLeafValues(doiNode.getChildren());
for (String value : values) {
@@ -837,6 +860,9 @@ public class MappingGenerationService {
return false;
}
+ /**
+ * 从模板 LnClassList 中查找当前 LN 的业务分组配置。
+ */
private DefaultTemplate.LnClassCfgItem findLnClassCfg(DefaultTemplate template, String lnClass) {
if (template == null || template.getLnClassList() == null) {
return null;
@@ -850,6 +876,9 @@ public class MappingGenerationService {
}
+ /**
+ * 在候选分析结果中定位当前前端回传的业务分组。
+ */
private IndexCandidate findCandidate(IndexAnalysis analysis, String groupKey, String groupDesc) {
if (analysis == null || analysis.getCandidates() == null) {
return null;
@@ -862,6 +891,9 @@ public class MappingGenerationService {
return null;
}
+ /**
+ * 递归收集 DOI 子树下所有 Val 文本,用于匹配倍率等模板配置。
+ */
private Set collectAllLeafValues(List nodes) {
Set result = new LinkedHashSet();
if (nodes == null) {
@@ -880,6 +912,9 @@ public class MappingGenerationService {
return result;
}
+ /**
+ * 从模板 UnitList 中查找指定 DAI 值对应的单位配置。
+ */
private DefaultTemplate.UnitCfgItem findUnitCfg(DefaultTemplate template, String value) {
if (template == null || template.getUnitList() == null) {
return null;
@@ -892,6 +927,9 @@ public class MappingGenerationService {
return null;
}
+ /**
+ * 从模板 MultiplierList 中查找指定 DAI 值对应的倍率配置。
+ */
private DefaultTemplate.MultiplierCfgItem findMultiplierCfg(DefaultTemplate template, String value) {
if (template == null || template.getMultiplierList() == null) {
return null;
@@ -904,6 +942,9 @@ public class MappingGenerationService {
return null;
}
+ /**
+ * 将相别节点名称转换为模板中的中文描述。
+ */
private String resolvePhaseDesc(DefaultTemplate template, String value) {
if (template == null || template.getPhaseList() == null) {
return value;
@@ -916,6 +957,9 @@ public class MappingGenerationService {
return value;
}
+ /**
+ * 将类型节点名称转换为模板中的中文描述。
+ */
private String resolveTypeDesc(DefaultTemplate template, String value) {
if (template == null || template.getTypeList() == null) {
return value;
@@ -929,6 +973,9 @@ public class MappingGenerationService {
}
+ /**
+ * 空安全、去首尾空白的字符串比较。
+ */
private boolean equalsTrim(String left, String right) {
if (left == null && right == null) {
return true;
@@ -1002,6 +1049,9 @@ public class MappingGenerationService {
return "96";
}
+ /**
+ * 解析输出版本号;请求未传时使用当天日期。
+ */
private String resolveVersion(String version) {
if (version != null && !version.trim().isEmpty()) {
return version.trim();
@@ -1009,6 +1059,9 @@ public class MappingGenerationService {
return LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE);
}
+ /**
+ * 按原 C# 规则转换 reportCount:单报告写 0,多报告写实际数量。
+ */
private int resolveReportCount(int size) {
return size <= 1 ? 0 : size;
}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/MappingRequestConverter.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/MappingRequestConverter.java
new file mode 100644
index 0000000..f2fb8ab
--- /dev/null
+++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/MappingRequestConverter.java
@@ -0,0 +1,132 @@
+package com.njcn.gather.icd.mapping.component;
+
+import com.njcn.gather.icd.mapping.config.MappingModuleConfig;
+import com.njcn.gather.icd.mapping.pojo.dto.GenerateFromIcdCommand;
+import com.njcn.gather.icd.mapping.pojo.dto.IndexBindingCommand;
+import com.njcn.gather.icd.mapping.pojo.dto.IndexSelectionGroupCommand;
+import com.njcn.gather.icd.mapping.pojo.param.GenerateMappingFromIcdRequest;
+import com.njcn.gather.icd.mapping.pojo.param.IndexBindingRequest;
+import com.njcn.gather.icd.mapping.pojo.param.IndexSelectionGroupRequest;
+import com.njcn.gather.icd.mapping.pojo.param.SubmitIndexSelectionRequest;
+import org.springframework.stereotype.Component;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
+
+/**
+ * 请求转换器。
+ *
+ * 作用:
+ * 1. 将接口层 request 转成应用层 command;
+ * 2. 统一处理 null 和空集合,避免后续业务层到处判空。
+ */
+@Component
+public class MappingRequestConverter {
+
+ /** 模块默认配置,用于补齐作者、输出目录等缺省值。 */
+ private final MappingModuleConfig moduleConfig;
+
+ public MappingRequestConverter(MappingModuleConfig moduleConfig) {
+ this.moduleConfig = moduleConfig;
+ }
+
+ /**
+ * 将候选接口上传文件转换为应用层命令,其他参数走模块默认值。
+ */
+ public GenerateFromIcdCommand toCommand(MultipartFile icdFile) {
+ return toCommand(icdFile, null);
+ }
+
+ /**
+ * 将上传文件和请求体转换为应用层命令。
+ */
+ public GenerateFromIcdCommand toCommand(MultipartFile icdFile, GenerateMappingFromIcdRequest request) {
+ try {
+ if (icdFile == null || icdFile.isEmpty()) {
+ throw new IllegalArgumentException("ICD 文件不能为空");
+ }
+ GenerateFromIcdCommand command = new GenerateFromIcdCommand();
+ command.setFileName(icdFile.getOriginalFilename());
+ command.setFileBytes(icdFile.getBytes());
+ command.setVersion(request == null ? null : request.getVersion());
+ command.setAuthor(resolveText(request == null ? null : request.getAuthor(), moduleConfig.getDefaultAuthor()));
+ command.setSaveToDisk(request != null && request.isSaveToDisk());
+ command.setPrettyJson(request != null && request.isPrettyJson());
+ command.setOutputDir(resolveText(request == null ? null : request.getOutputDir(), moduleConfig.getDefaultOutputDir()));
+ fillIndexSelection(command, request == null ? null : request.getIndexSelection());
+ return command;
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("请求转换失败:" + ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * 将提交绑定接口请求体转换为应用层命令。
+ */
+ public GenerateFromIcdCommand toCommand(SubmitIndexSelectionRequest request) {
+ try {
+ if (request == null) {
+ throw new IllegalArgumentException("请求体不能为空");
+ }
+ if (request.getIcdDocument() == null) {
+ throw new IllegalArgumentException("ICD 解析结果不能为空");
+ }
+
+ GenerateFromIcdCommand command = new GenerateFromIcdCommand();
+ command.setIcdDocument(request.getIcdDocument());
+ command.setFileName(request.getIcdDocument().getFileName());
+ command.setVersion(request.getVersion());
+ command.setAuthor(resolveText(request.getAuthor(), moduleConfig.getDefaultAuthor()));
+ command.setSaveToDisk(request.isSaveToDisk());
+ command.setPrettyJson(request.isPrettyJson());
+ command.setOutputDir(resolveText(request.getOutputDir(), moduleConfig.getDefaultOutputDir()));
+ fillIndexSelection(command, request.getIndexSelection());
+ return command;
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("请求转换失败:" + ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * 复制前端提交的索引绑定结构。
+ */
+ private void fillIndexSelection(GenerateFromIcdCommand command, List indexSelection) {
+ if (indexSelection == null) {
+ return;
+ }
+ for (IndexSelectionGroupRequest groupRequest : indexSelection) {
+ if (groupRequest == null) {
+ continue;
+ }
+ IndexSelectionGroupCommand groupCommand = new IndexSelectionGroupCommand();
+ groupCommand.setGroupKey(groupRequest.getGroupKey());
+ groupCommand.setGroupDesc(groupRequest.getGroupDesc());
+
+ if (groupRequest.getBindings() != null) {
+ for (IndexBindingRequest bindingRequest : groupRequest.getBindings()) {
+ if (bindingRequest == null) {
+ continue;
+ }
+ IndexBindingCommand bindingCommand = new IndexBindingCommand();
+ bindingCommand.setReportName(bindingRequest.getReportName());
+ bindingCommand.setDataSetName(bindingRequest.getDataSetName());
+ bindingCommand.setLabel(bindingRequest.getLabel());
+ bindingCommand.setLnInst(bindingRequest.getLnInst());
+ groupCommand.getBindings().add(bindingCommand);
+ }
+ }
+
+ command.getIndexSelection().add(groupCommand);
+ }
+ }
+
+ /**
+ * 统一处理文本参数的 trim 和默认值回退。
+ */
+ private String resolveText(String value, String defaultValue) {
+ if (value != null && !value.trim().isEmpty()) {
+ return value.trim();
+ }
+ return defaultValue == null ? null : defaultValue.trim();
+ }
+}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/MappingResponseConverter.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/MappingResponseConverter.java
new file mode 100644
index 0000000..0b63e05
--- /dev/null
+++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/MappingResponseConverter.java
@@ -0,0 +1,85 @@
+package com.njcn.gather.icd.mapping.component;
+
+import com.njcn.gather.icd.mapping.pojo.bo.GenerateMappingResult;
+import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexCandidate;
+import com.njcn.gather.icd.mapping.pojo.bo.analysis.IndexCandidateReportItem;
+import com.njcn.gather.icd.mapping.pojo.enums.GenerateStatus;
+import com.njcn.gather.icd.mapping.pojo.vo.IndexCandidateReportItemResponse;
+import com.njcn.gather.icd.mapping.pojo.vo.IndexCandidateResponse;
+import com.njcn.gather.icd.mapping.pojo.vo.MappingTaskResponse;
+import org.springframework.stereotype.Component;
+
+/**
+ * 接口响应转换器。
+ *
+ * 按接口阶段仅组装当前场景必需字段,避免返回冗余信息。
+ */
+@Component
+public class MappingResponseConverter {
+
+ /**
+ * 候选接口响应。
+ */
+ public MappingTaskResponse fromCandidateResult(GenerateMappingResult result) {
+ MappingTaskResponse response = initBaseResponse(result);
+ response.setIcdDocument(result.getIcdDocument());
+ fillIndexCandidates(response, result);
+ return response;
+ }
+
+ /**
+ * 正式提交类接口响应。
+ *
+ * SUCCESS:仅返回最终结果;
+ * NEED_INDEX_SELECTION:返回重新选择所需的候选信息;
+ * FAILED:仅返回错误信息。
+ */
+ public MappingTaskResponse fromSubmitResult(GenerateMappingResult result) {
+ MappingTaskResponse response = initBaseResponse(result);
+ if (result.getStatus() == GenerateStatus.SUCCESS) {
+ response.setMappingJson(result.getMappingJson());
+ response.setSavedPath(result.getSavedPath());
+ return response;
+ }
+
+ if (result.getStatus() == GenerateStatus.NEED_INDEX_SELECTION) {
+ response.setIcdDocument(result.getIcdDocument());
+ fillIndexCandidates(response, result);
+ }
+ return response;
+ }
+
+ private MappingTaskResponse initBaseResponse(GenerateMappingResult result) {
+ MappingTaskResponse response = new MappingTaskResponse();
+ response.setStatus(result.getStatus());
+ response.setMessage(result.getMessage());
+ response.getProblems().addAll(result.getProblems());
+ return response;
+ }
+
+ private void fillIndexCandidates(MappingTaskResponse response, GenerateMappingResult result) {
+ if (result.getIndexAnalysis() == null || result.getIndexAnalysis().getCandidates() == null) {
+ return;
+ }
+ for (IndexCandidate candidate : result.getIndexAnalysis().getCandidates()) {
+ IndexCandidateResponse candidateResponse = new IndexCandidateResponse();
+ candidateResponse.setGroupKey(candidate.getGroupKey());
+ candidateResponse.setGroupDesc(candidate.getGroupDesc());
+ candidateResponse.setReportCount(candidate.getReportCount());
+ candidateResponse.getTemplateLabels().addAll(candidate.getTemplateLabels());
+
+ if (candidate.getReports() != null) {
+ for (IndexCandidateReportItem item : candidate.getReports()) {
+ IndexCandidateReportItemResponse itemResponse = new IndexCandidateReportItemResponse();
+ itemResponse.setReportName(item.getReportName());
+ itemResponse.setDataSetName(item.getDataSetName());
+ itemResponse.setReportDesc(item.getReportDesc());
+ itemResponse.getAvailableLnInstValues().addAll(item.getAvailableLnInstValues());
+ candidateResponse.getReports().add(itemResponse);
+ }
+ }
+
+ response.getIndexCandidates().add(candidateResponse);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/config/MappingModuleConfig.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/config/MappingModuleConfig.java
index 3021ca7..23d56d6 100644
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/config/MappingModuleConfig.java
+++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/config/MappingModuleConfig.java
@@ -1,17 +1,18 @@
package com.njcn.gather.icd.mapping.config;
+import lombok.Getter;
+import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
- * 模块配置。
- * 模块配置类。集中管理默认作者、默认模板路径等可配置项。
+ * `mms-mapping` 模块配置。
*
- * 说明:
- * 1. 这里把模板路径、输出目录、默认作者等集中管理。
- * 2. 当前先用 @Value + 默认值,后续你也可以改成 @ConfigurationProperties。
+ * 统一管理默认模板路径、默认作者和默认输出目录等配置项。
*/
@Component
+@Getter
+@Setter
public class MappingModuleConfig {
/** 默认模板资源路径。 */
@@ -25,28 +26,4 @@ public class MappingModuleConfig {
/** 默认输出目录。 */
@Value("${icd.mapping.default-output-dir:}")
private String defaultOutputDir;
-
- public String getDefaultTemplatePath() {
- return defaultTemplatePath;
- }
-
- public void setDefaultTemplatePath(String defaultTemplatePath) {
- this.defaultTemplatePath = defaultTemplatePath;
- }
-
- public String getDefaultAuthor() {
- return defaultAuthor;
- }
-
- public void setDefaultAuthor(String defaultAuthor) {
- this.defaultAuthor = defaultAuthor;
- }
-
- public String getDefaultOutputDir() {
- return defaultOutputDir;
- }
-
- public void setDefaultOutputDir(String defaultOutputDir) {
- this.defaultOutputDir = defaultOutputDir;
- }
-}
\ No newline at end of file
+}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/MappingController.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/MappingController.java
index 292057d..7e21674 100644
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/MappingController.java
+++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/MappingController.java
@@ -1,50 +1,92 @@
package com.njcn.gather.icd.mapping.controller;
-import com.njcn.gather.icd.mapping.application.MappingTaskAppService;
-import com.njcn.gather.icd.mapping.application.command.GenerateFromIcdCommand;
-import com.njcn.gather.icd.mapping.application.result.GenerateMappingResult;
-import com.njcn.gather.icd.mapping.controller.request.GenerateMappingFromIcdRequest;
-import com.njcn.gather.icd.mapping.controller.response.MappingTaskResponse;
-import com.njcn.gather.icd.mapping.converter.MappingRequestConverter;
-import com.njcn.gather.icd.mapping.converter.MappingResponseConverter;
+import com.njcn.common.pojo.annotation.OperateInfo;
+import com.njcn.common.pojo.enums.common.LogEnum;
+import com.njcn.common.utils.LogUtil;
+import com.njcn.gather.icd.mapping.component.MappingRequestConverter;
+import com.njcn.gather.icd.mapping.component.MappingResponseConverter;
+import com.njcn.gather.icd.mapping.pojo.bo.GenerateMappingResult;
+import com.njcn.gather.icd.mapping.pojo.dto.GenerateFromIcdCommand;
+import com.njcn.gather.icd.mapping.pojo.param.GenerateMappingFromIcdRequest;
+import com.njcn.gather.icd.mapping.pojo.param.SubmitIndexSelectionRequest;
+import com.njcn.gather.icd.mapping.pojo.vo.MappingTaskResponse;
+import com.njcn.gather.icd.mapping.service.MappingTaskService;
+import com.njcn.gather.icd.mapping.utils.DateUtils;
+import com.njcn.web.controller.BaseController;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
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.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
/**
- * ICD 映射接口。
+ * ICD 映射接口入口。
*/
+@Slf4j
+@Api(tags = "ICD 映射")
@RestController
@RequestMapping("/api/mms-mapping")
-public class MappingController {
+@RequiredArgsConstructor
+public class MappingController extends BaseController {
- private final MappingTaskAppService mappingTaskAppService;
+ /** 映射任务编排服务,负责从 ICD 解析到映射生成的完整链路。 */
+ private final MappingTaskService mappingTaskService;
+
+ /** 请求参数转换器,将接口入参转换为应用层命令。 */
private final MappingRequestConverter requestConverter;
+
+ /** 响应转换器,按接口阶段裁剪最小返回字段。 */
private final MappingResponseConverter responseConverter;
- public MappingController(MappingTaskAppService mappingTaskAppService,
- MappingRequestConverter requestConverter,
- MappingResponseConverter responseConverter) {
- this.mappingTaskAppService = mappingTaskAppService;
- this.requestConverter = requestConverter;
- this.responseConverter = responseConverter;
+ /**
+ * 上传 ICD 文件,返回候选结果和可编辑的 ICD 解析结果。
+ */
+ @OperateInfo(info = LogEnum.BUSINESS_COMMON)
+ @ApiOperation("上传 ICD 文件并生成索引候选")
+ @PostMapping(value = "/get-icd", consumes = {"multipart/form-data"})
+ public MappingTaskResponse getICD(@RequestPart("icdFile") MultipartFile icdFile) {
+ String methodDescribe = getMethodDescribe("getICD");
+ LogUtil.njcnDebug(log, "{},开始解析 ICD 文件并生成索引候选,fileName={}", methodDescribe, DateUtils.resolveFileName(icdFile));
+ GenerateFromIcdCommand command = requestConverter.toCommand(icdFile);
+ GenerateMappingResult result = mappingTaskService.getICD(command);
+ return responseConverter.fromCandidateResult(result);
}
/**
- * 上传 ICD 并生成映射。
- *
- * 表单参数:
- * 1. icdFile:ICD 文件
- * 2. request:JSON 请求体
+ * 根据前端确认后的索引绑定关系生成 MMS JSON。
*/
- @PostMapping(value = "/generate-from-icd", consumes = {"multipart/form-data"})
- public MappingTaskResponse generateFromIcd(@RequestPart("icdFile") MultipartFile icdFile,
- @Validated @RequestPart("request") GenerateMappingFromIcdRequest request) {
+ @OperateInfo(info = LogEnum.BUSINESS_COMMON)
+ @ApiOperation("获取 MMS JSON")
+ @PostMapping("/get-mms-json")
+ public MappingTaskResponse getMmsJson(@Validated @RequestBody SubmitIndexSelectionRequest request) {
+ String methodDescribe = getMethodDescribe("getMmsJson");
+ LogUtil.njcnDebug(log, "{},开始生成 MMS JSON,indexSelectionCount={}",
+ methodDescribe, DateUtils.resolveSelectionCount(request == null ? null : request.getIndexSelection()));
+ GenerateFromIcdCommand command = requestConverter.toCommand(request);
+ GenerateMappingResult result = mappingTaskService.getMmsJson(command);
+ return responseConverter.fromSubmitResult(result);
+ }
+
+ /**
+ * 上传 ICD 后直接串联候选生成和索引提交,统一返回正式提交阶段结果。
+ */
+ @OperateInfo(info = LogEnum.BUSINESS_COMMON)
+ @ApiOperation("上传 ICD 后直接获取 MMS JSON")
+ @PostMapping(value = "/get-icd-mms-json", consumes = {"multipart/form-data"})
+ public MappingTaskResponse getIcdMmsJson(@RequestPart("icdFile") MultipartFile icdFile,
+ @Validated @RequestPart("request") GenerateMappingFromIcdRequest request) {
+ String methodDescribe = getMethodDescribe("getIcdMmsJson");
+ LogUtil.njcnDebug(log, "{},开始获取 ICD MMS JSON,fileName={}, indexSelectionCount={}",
+ methodDescribe, DateUtils.resolveFileName(icdFile),
+ DateUtils.resolveSelectionCount(request == null ? null : request.getIndexSelection()));
GenerateFromIcdCommand command = requestConverter.toCommand(icdFile, request);
- GenerateMappingResult result = mappingTaskAppService.generateFromIcd(command);
- return responseConverter.fromResult(result);
+ GenerateMappingResult result = mappingTaskService.getIcdMmsJson(command);
+ return responseConverter.fromSubmitResult(result);
}
}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/request/GenerateMappingFromIcdRequest.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/request/GenerateMappingFromIcdRequest.java
deleted file mode 100644
index e9f86de..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/request/GenerateMappingFromIcdRequest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-package com.njcn.gather.icd.mapping.controller.request;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 生成映射接口请求体。
- *
- * 说明:
- * 1. 旧版结构中,indexSelection 是 Map,只能表达“一个报告对应一个值”,
- * 无法表达“一个业务分组下有多个报告、一个报告下又有多条标签绑定”的真实场景。
- * 2. 新版结构改成 List,用来完整承载用户在前端完成的绑定结果。
- * 3. 第一次只上传 ICD 时,这个字段可以为空;第二次用户确认绑定后再把完整结构上送即可。
- */
-public class GenerateMappingFromIcdRequest {
-
- /** 输出版本号。为空时后端默认补 1.0。 */
- private String version;
-
- /** 作者。为空时默认空字符串。 */
- private String author;
-
- /** 是否保存到磁盘。 */
- private boolean saveToDisk;
-
- /** 是否返回美化 JSON。 */
- private boolean prettyJson;
-
- /** 输出目录。saveToDisk=true 时才会用到。 */
- private String outputDir;
-
- /**
- * 索引选择结果。
- *
- * 说明:
- * 1. 每一个元素代表一个“业务分组”,例如:实时数据、统计数据、波动闪变。
- * 2. 每个业务分组下面又包含多条绑定关系。
- * 3. 允许为空;为空时后端返回 NEED_INDEX_SELECTION,给前端候选参考项。
- */
- private List indexSelection = new ArrayList();
-
- public String getVersion() {
- return version;
- }
-
- public void setVersion(String version) {
- this.version = version;
- }
-
- public String getAuthor() {
- return author;
- }
-
- public void setAuthor(String author) {
- this.author = author;
- }
-
- public boolean isSaveToDisk() {
- return saveToDisk;
- }
-
- public void setSaveToDisk(boolean saveToDisk) {
- this.saveToDisk = saveToDisk;
- }
-
- public boolean isPrettyJson() {
- return prettyJson;
- }
-
- public void setPrettyJson(boolean prettyJson) {
- this.prettyJson = prettyJson;
- }
-
- public String getOutputDir() {
- return outputDir;
- }
-
- public void setOutputDir(String outputDir) {
- this.outputDir = outputDir;
- }
-
- public List getIndexSelection() {
- return indexSelection;
- }
-
- public void setIndexSelection(List indexSelection) {
- this.indexSelection = indexSelection;
- }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/request/IndexBindingRequest.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/request/IndexBindingRequest.java
deleted file mode 100644
index 7f64576..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/request/IndexBindingRequest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package com.njcn.gather.icd.mapping.controller.request;
-
-/**
- * 单条索引绑定请求。
- *
- * 一条绑定只表达一个最小关系:
- * 某个报告(reportName)下,使用某个标签(label)与某个 lnInst 数字做绑定。
- *
- * 这样做的好处:
- * 1. 一个报告可以出现多次,对应多个标签。
- * 2. 一个业务分组下也可以有多个报告。
- * 3. 后端校验、生成映射时都更容易逐条处理。
- */
-public class IndexBindingRequest {
-
- /** 绑定发生在哪个报告上,例如:brcbStHarm。 */
- private String reportName;
-
- /** 绑定发生在哪个数据集上,例如:dsStHarm。 */
- private String dataSetName;
-
- /** 业务标签,例如:最大值、最小值、实时数据。 */
- private String label;
-
- /** 当前标签最终绑定到的 lnInst 数字,例如:1、2、8。 */
- private String lnInst;
-
- public String getReportName() {
- return reportName;
- }
-
- public void setReportName(String reportName) {
- this.reportName = reportName;
- }
-
- public String getDataSetName() {
- return dataSetName;
- }
-
- public void setDataSetName(String dataSetName) {
- this.dataSetName = dataSetName;
- }
-
- public String getLabel() {
- return label;
- }
-
- public void setLabel(String label) {
- this.label = label;
- }
-
- public String getLnInst() {
- return lnInst;
- }
-
- public void setLnInst(String lnInst) {
- this.lnInst = lnInst;
- }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/request/IndexSelectionGroupRequest.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/request/IndexSelectionGroupRequest.java
deleted file mode 100644
index 5d2b77e..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/request/IndexSelectionGroupRequest.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.njcn.gather.icd.mapping.controller.request;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 单个业务分组的索引选择请求。
- *
- * 例如:
- * - groupKey = REALTIME_DATA
- * - groupDesc = 实时数据
- * - bindings = 多条“报告 + 标签 + lnInst”的绑定关系
- */
-public class IndexSelectionGroupRequest {
-
- /**
- * 分组唯一键。
- *
- * 说明:
- * 这个值由后端在 NEED_INDEX_SELECTION 时返回,前端原样带回即可,避免仅靠中文描述做匹配。
- */
- private String groupKey;
-
- /** 分组中文描述,例如:实时数据、统计数据。 */
- private String groupDesc;
-
- /** 当前业务分组下,用户最终确认的绑定关系。 */
- private List bindings = new ArrayList();
-
- public String getGroupKey() {
- return groupKey;
- }
-
- public void setGroupKey(String groupKey) {
- this.groupKey = groupKey;
- }
-
- public String getGroupDesc() {
- return groupDesc;
- }
-
- public void setGroupDesc(String groupDesc) {
- this.groupDesc = groupDesc;
- }
-
- public List getBindings() {
- return bindings;
- }
-
- public void setBindings(List bindings) {
- this.bindings = bindings;
- }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/response/IndexCandidateReportItemResponse.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/response/IndexCandidateReportItemResponse.java
deleted file mode 100644
index 687b7ff..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/response/IndexCandidateReportItemResponse.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.njcn.gather.icd.mapping.controller.response;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 索引候选分组下的单个报告响应。
- */
-public class IndexCandidateReportItemResponse {
-
- /** 报告名称。 */
- private String reportName;
-
- /** 数据集名称。 */
- private String dataSetName;
-
- /** 报告描述。 */
- private String reportDesc;
-
- /** 当前报告可选的 lnInst 数字。 */
- private List availableLnInstValues = new ArrayList();
-
- public String getReportName() {
- return reportName;
- }
-
- public void setReportName(String reportName) {
- this.reportName = reportName;
- }
-
- public String getDataSetName() {
- return dataSetName;
- }
-
- public void setDataSetName(String dataSetName) {
- this.dataSetName = dataSetName;
- }
-
- public String getReportDesc() {
- return reportDesc;
- }
-
- public void setReportDesc(String reportDesc) {
- this.reportDesc = reportDesc;
- }
-
- public List getAvailableLnInstValues() {
- return availableLnInstValues;
- }
-
- public void setAvailableLnInstValues(List availableLnInstValues) {
- this.availableLnInstValues = availableLnInstValues;
- }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/response/IndexCandidateResponse.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/response/IndexCandidateResponse.java
deleted file mode 100644
index d6c9f74..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/response/IndexCandidateResponse.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package com.njcn.gather.icd.mapping.controller.response;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 索引候选返回对象。
- *
- * 说明:
- * 这是给前端“待匹配界面”使用的正式结构:
- * - 一个候选就是一个业务分组;
- * - 分组下面再挂多个报告;
- * - 前端根据 templateLabels 与 reports[*].availableLnInstValues 做人工绑定。
- */
-public class IndexCandidateResponse {
-
- /** 分组唯一键。 */
- private String groupKey;
-
- /** 分组中文描述。 */
- private String groupDesc;
-
- /** 当前分组包含的报告数。 */
- private int reportCount;
-
- /** 模板里配置的可选标签。 */
- private List templateLabels = new ArrayList();
-
- /** 当前分组下的报告候选列表。 */
- private List reports = new ArrayList();
-
- public String getGroupKey() {
- return groupKey;
- }
-
- public void setGroupKey(String groupKey) {
- this.groupKey = groupKey;
- }
-
- public String getGroupDesc() {
- return groupDesc;
- }
-
- public void setGroupDesc(String groupDesc) {
- this.groupDesc = groupDesc;
- }
-
- public int getReportCount() {
- return reportCount;
- }
-
- public void setReportCount(int reportCount) {
- this.reportCount = reportCount;
- }
-
- public List getTemplateLabels() {
- return templateLabels;
- }
-
- public void setTemplateLabels(List templateLabels) {
- this.templateLabels = templateLabels;
- }
-
- public List getReports() {
- return reports;
- }
-
- public void setReports(List reports) {
- this.reports = reports;
- }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/response/MappingDocumentResponse.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/response/MappingDocumentResponse.java
deleted file mode 100644
index 31fcfd9..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/response/MappingDocumentResponse.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.njcn.gather.icd.mapping.controller.response;
-
-/**
- * 映射摘要响应。
- * 映射摘要响应 DTO。用于把最终映射的关键信息单独封装返回。
- */
-public class MappingDocumentResponse {
- private String version;
- private String author;
- private String ied;
- private String ld;
- private int reportCount;
- private int dataSetCount;
- public String getVersion() { return version; }
- public void setVersion(String version) { this.version = version; }
- public String getAuthor() { return author; }
- public void setAuthor(String author) { this.author = author; }
- public String getIed() { return ied; }
- public void setIed(String ied) { this.ied = ied; }
- public String getLd() { return ld; }
- public void setLd(String ld) { this.ld = ld; }
- public int getReportCount() { return reportCount; }
- public void setReportCount(int reportCount) { this.reportCount = reportCount; }
- public int getDataSetCount() { return dataSetCount; }
- public void setDataSetCount(int dataSetCount) { this.dataSetCount = dataSetCount; }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/response/MappingTaskResponse.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/response/MappingTaskResponse.java
deleted file mode 100644
index 1b10c01..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/controller/response/MappingTaskResponse.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.njcn.gather.icd.mapping.controller.response;
-
-import com.njcn.gather.icd.mapping.enums.GenerateStatus;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 第一个接口统一响应。
- * 接口统一响应 DTO。返回生成状态、映射内容、候选索引、问题列表等。
- */
-public class MappingTaskResponse {
- private GenerateStatus status;
- private String message;
- private String iedName;
- private String ldInst;
- private String mappingJson;
- private String savedPath;
- private MappingDocumentResponse mappingDocument;
- private List indexCandidates = new ArrayList();
- private List problems = new ArrayList();
- public GenerateStatus getStatus() { return status; }
- public void setStatus(GenerateStatus status) { this.status = status; }
- public String getMessage() { return message; }
- public void setMessage(String message) { this.message = message; }
- public String getIedName() { return iedName; }
- public void setIedName(String iedName) { this.iedName = iedName; }
- public String getLdInst() { return ldInst; }
- public void setLdInst(String ldInst) { this.ldInst = ldInst; }
- public String getMappingJson() { return mappingJson; }
- public void setMappingJson(String mappingJson) { this.mappingJson = mappingJson; }
- public String getSavedPath() { return savedPath; }
- public void setSavedPath(String savedPath) { this.savedPath = savedPath; }
- public MappingDocumentResponse getMappingDocument() { return mappingDocument; }
- public void setMappingDocument(MappingDocumentResponse mappingDocument) { this.mappingDocument = mappingDocument; }
- public List getIndexCandidates() { return indexCandidates; }
- public void setIndexCandidates(List indexCandidates) { this.indexCandidates = indexCandidates; }
- public List getProblems() { return problems; }
- public void setProblems(List problems) { this.problems = problems; }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/converter/MappingRequestConverter.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/converter/MappingRequestConverter.java
deleted file mode 100644
index d57d816..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/converter/MappingRequestConverter.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package com.njcn.gather.icd.mapping.converter;
-
-import com.njcn.gather.icd.mapping.application.command.GenerateFromIcdCommand;
-import com.njcn.gather.icd.mapping.application.command.IndexBindingCommand;
-import com.njcn.gather.icd.mapping.application.command.IndexSelectionGroupCommand;
-import com.njcn.gather.icd.mapping.config.MappingModuleConfig;
-import com.njcn.gather.icd.mapping.controller.request.GenerateMappingFromIcdRequest;
-import com.njcn.gather.icd.mapping.controller.request.IndexBindingRequest;
-import com.njcn.gather.icd.mapping.controller.request.IndexSelectionGroupRequest;
-import org.springframework.stereotype.Component;
-import org.springframework.web.multipart.MultipartFile;
-
-/**
- * 请求转换器。
- *
- * 作用:
- * 1. 把接口层 request 转成应用层 command。
- * 2. 统一处理 null 和空集合,避免后面业务层到处判空。
- */
-@Component
-public class MappingRequestConverter {
-
- private final MappingModuleConfig moduleConfig;
-
- public MappingRequestConverter(MappingModuleConfig moduleConfig) {
- this.moduleConfig = moduleConfig;
- }
-
- public GenerateFromIcdCommand toCommand(MultipartFile icdFile, GenerateMappingFromIcdRequest request) {
- try {
- if (icdFile == null || icdFile.isEmpty()) {
- throw new IllegalArgumentException("ICD 文件不能为空");
- }
- GenerateFromIcdCommand command = new GenerateFromIcdCommand();
- command.setFileName(icdFile.getOriginalFilename());
- command.setFileBytes(icdFile.getBytes());
- command.setVersion(request == null ? null : request.getVersion());
- command.setAuthor(resolveText(request == null ? null : request.getAuthor(), moduleConfig.getDefaultAuthor()));
- command.setSaveToDisk(request != null && request.isSaveToDisk());
- command.setPrettyJson(request == null || request.isPrettyJson());
- command.setOutputDir(resolveText(request == null ? null : request.getOutputDir(), moduleConfig.getDefaultOutputDir()));
-
- if (request != null && request.getIndexSelection() != null) {
- for (IndexSelectionGroupRequest groupRequest : request.getIndexSelection()) {
- if (groupRequest == null) {
- continue;
- }
- IndexSelectionGroupCommand groupCommand = new IndexSelectionGroupCommand();
- groupCommand.setGroupKey(groupRequest.getGroupKey());
- groupCommand.setGroupDesc(groupRequest.getGroupDesc());
-
- if (groupRequest.getBindings() != null) {
- for (IndexBindingRequest bindingRequest : groupRequest.getBindings()) {
- if (bindingRequest == null) {
- continue;
- }
- IndexBindingCommand bindingCommand = new IndexBindingCommand();
- bindingCommand.setReportName(bindingRequest.getReportName());
- bindingCommand.setDataSetName(bindingRequest.getDataSetName());
- bindingCommand.setLabel(bindingRequest.getLabel());
- bindingCommand.setLnInst(bindingRequest.getLnInst());
- groupCommand.getBindings().add(bindingCommand);
- }
- }
-
- command.getIndexSelection().add(groupCommand);
- }
- }
-
- return command;
- } catch (Exception ex) {
- throw new IllegalArgumentException("请求转换失败:" + ex.getMessage(), ex);
- }
- }
-
- private String resolveText(String value, String defaultValue) {
- if (value != null && !value.trim().isEmpty()) {
- return value.trim();
- }
- return defaultValue == null ? null : defaultValue.trim();
- }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/converter/MappingResponseConverter.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/converter/MappingResponseConverter.java
deleted file mode 100644
index ff6844a..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/converter/MappingResponseConverter.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package com.njcn.gather.icd.mapping.converter;
-
-import com.njcn.gather.icd.mapping.application.result.GenerateMappingResult;
-import com.njcn.gather.icd.mapping.controller.response.IndexCandidateReportItemResponse;
-import com.njcn.gather.icd.mapping.controller.response.IndexCandidateResponse;
-import com.njcn.gather.icd.mapping.controller.response.MappingDocumentResponse;
-import com.njcn.gather.icd.mapping.controller.response.MappingTaskResponse;
-import com.njcn.gather.icd.mapping.domain.model.analysis.IndexCandidate;
-import com.njcn.gather.icd.mapping.domain.model.analysis.IndexCandidateReportItem;
-import org.springframework.stereotype.Component;
-
-/**
- * 响应转换器。
- *
- * 作用:
- * 1. 把应用层结果转换成接口层响应对象;
- * 2. 对待匹配索引场景,输出新的“按业务分组返回”的结构。
- */
-@Component
-public class MappingResponseConverter {
-
- public MappingTaskResponse fromResult(GenerateMappingResult result) {
- MappingTaskResponse response = new MappingTaskResponse();
- response.setStatus(result.getStatus());
- response.setMessage(result.getMessage());
- response.setIedName(result.getIedName());
- response.setLdInst(result.getLdInst());
- response.setMappingJson(result.getMappingJson());
- response.setSavedPath(result.getSavedPath());
- response.getProblems().addAll(result.getProblems());
-
- if (result.getIndexAnalysis() != null && result.getIndexAnalysis().getCandidates() != null) {
- for (IndexCandidate candidate : result.getIndexAnalysis().getCandidates()) {
- IndexCandidateResponse candidateResponse = new IndexCandidateResponse();
- candidateResponse.setGroupKey(candidate.getGroupKey());
- candidateResponse.setGroupDesc(candidate.getGroupDesc());
- candidateResponse.setReportCount(candidate.getReportCount());
- candidateResponse.getTemplateLabels().addAll(candidate.getTemplateLabels());
-
- if (candidate.getReports() != null) {
- for (IndexCandidateReportItem item : candidate.getReports()) {
- IndexCandidateReportItemResponse itemResponse = new IndexCandidateReportItemResponse();
- itemResponse.setReportName(item.getReportName());
- itemResponse.setDataSetName(item.getDataSetName());
- itemResponse.setReportDesc(item.getReportDesc());
- itemResponse.getAvailableLnInstValues().addAll(item.getAvailableLnInstValues());
- candidateResponse.getReports().add(itemResponse);
- }
- }
-
- response.getIndexCandidates().add(candidateResponse);
- }
- }
-
- if (result.getMappingDocument() != null) {
- MappingDocumentResponse doc = new MappingDocumentResponse();
- doc.setVersion(result.getMappingDocument().getVersion());
- doc.setAuthor(result.getMappingDocument().getAuthor());
- doc.setIed(result.getMappingDocument().getIed());
- doc.setLd(result.getMappingDocument().getLd());
- doc.setReportCount(result.getMappingDocument().getReportMap() == null
- ? 0 : result.getMappingDocument().getReportMap().size());
- doc.setDataSetCount(result.getMappingDocument().getDataSetList() == null
- ? 0 : result.getMappingDocument().getDataSetList().size());
- response.setMappingDocument(doc);
- }
-
- return response;
- }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/analysis/IndexCandidate.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/analysis/IndexCandidate.java
deleted file mode 100644
index 957ca83..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/analysis/IndexCandidate.java
+++ /dev/null
@@ -1,113 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.analysis;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 索引候选分组。
- *
- * 说明:
- * 1. 一条候选对应一个业务分组,例如:统计数据、实时数据;
- * 2. 一个业务分组下可以包含多个报告;
- * 3. 这里不仅保存返回给前端的候选项,也保存从 DefaultCfg.ReportList 带下来的配置项,
- * 供后续 MappingGenerationService 直接使用,避免“二次查模板”失败。
- */
-public class IndexCandidate {
-
- /** 分组唯一键。 */
- private String groupKey;
-
- /** 分组描述。 */
- private String groupDesc;
-
- /** 该分组下实际匹配到的报告数量。 */
- private int reportCount;
-
- /** DefaultCfg.txt 中该分组可用的标签模板。 */
- private List templateLabels = new ArrayList();
-
- /** 当前分组下匹配到的报告列表。 */
- private List reports = new ArrayList();
-
- /**
- * DefaultCfg.ReportList.inst
- * 例如:01 / 02 / 03 / 04
- */
- private String reportInst;
-
- /**
- * DefaultCfg.ReportList.Select
- * 例如:DataStatFileMap / DataRealFileMap / FlickerFileMap
- */
- private String select;
-
- /**
- * DefaultCfg.ReportList.TrgOps
- * 例如:40 / 96
- */
- private String trgOps;
-
- public String getGroupKey() {
- return groupKey;
- }
-
- public void setGroupKey(String groupKey) {
- this.groupKey = groupKey;
- }
-
- public String getGroupDesc() {
- return groupDesc;
- }
-
- public void setGroupDesc(String groupDesc) {
- this.groupDesc = groupDesc;
- }
-
- public int getReportCount() {
- return reportCount;
- }
-
- public void setReportCount(int reportCount) {
- this.reportCount = reportCount;
- }
-
- public List getTemplateLabels() {
- return templateLabels;
- }
-
- public void setTemplateLabels(List templateLabels) {
- this.templateLabels = templateLabels;
- }
-
- public List getReports() {
- return reports;
- }
-
- public void setReports(List reports) {
- this.reports = reports;
- }
-
- public String getReportInst() {
- return reportInst;
- }
-
- public void setReportInst(String reportInst) {
- this.reportInst = reportInst;
- }
-
- public String getSelect() {
- return select;
- }
-
- public void setSelect(String select) {
- this.select = select;
- }
-
- public String getTrgOps() {
- return trgOps;
- }
-
- public void setTrgOps(String trgOps) {
- this.trgOps = trgOps;
- }
-}
\ No newline at end of file
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/analysis/IndexCandidateReportItem.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/analysis/IndexCandidateReportItem.java
deleted file mode 100644
index 3620ae7..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/analysis/IndexCandidateReportItem.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.analysis;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 候选分组下的单个报告项。
- *
- * 这个对象专门给前端展示“这个分组下面有哪些报告,以及每个报告对应哪些可用 lnInst 数字”。
- */
-public class IndexCandidateReportItem {
-
- /** 报告名称。 */
- private String reportName;
-
- /** 数据集名称。 */
- private String dataSetName;
-
- /** 报告中文描述。一般和分组描述相同,保留它是为了前端渲染更灵活。 */
- private String reportDesc;
-
- /** 当前报告在 ICD 中解析出来的所有可选 lnInst 数字。 */
- private List availableLnInstValues = new ArrayList();
-
- public String getReportName() {
- return reportName;
- }
-
- public void setReportName(String reportName) {
- this.reportName = reportName;
- }
-
- public String getDataSetName() {
- return dataSetName;
- }
-
- public void setDataSetName(String dataSetName) {
- this.dataSetName = dataSetName;
- }
-
- public String getReportDesc() {
- return reportDesc;
- }
-
- public void setReportDesc(String reportDesc) {
- this.reportDesc = reportDesc;
- }
-
- public List getAvailableLnInstValues() {
- return availableLnInstValues;
- }
-
- public void setAvailableLnInstValues(List availableLnInstValues) {
- this.availableLnInstValues = availableLnInstValues;
- }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/analysis/ValidationResult.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/analysis/ValidationResult.java
deleted file mode 100644
index 1ed5c89..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/analysis/ValidationResult.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.analysis;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 校验结果。
- * 索引校验结果模型。保存是否通过以及问题列表。
- */
-public class ValidationResult {
- private boolean valid;
- private List problems = new ArrayList();
-
- public boolean isValid() { return valid; }
- public void setValid(boolean valid) { this.valid = valid; }
- public List getProblems() { return problems; }
- public void setProblems(List problems) { this.problems = problems; }
-}
\ No newline at end of file
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/DataSetNode.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/DataSetNode.java
deleted file mode 100644
index 04e7a31..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/DataSetNode.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.icd;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * DataSet 节点。
- * 数据集模型。用于承接 DataSet 下的 FCDA 列表。
- */
-public class DataSetNode {
- private String name;
- private List fcdas = new ArrayList();
- public String getName() { return name; }
- public void setName(String name) { this.name = name; }
- public List getFcdas() { return fcdas; }
- public void setFcdas(List fcdas) { this.fcdas = fcdas; }
-}
\ No newline at end of file
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/DoiElementNode.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/DoiElementNode.java
deleted file mode 100644
index 23cf301..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/DoiElementNode.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.icd;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * DOI/SDI/DAI 统一节点。
- * DOI/SDI/DAI 细项模型。用于递归承接 DOI 树明细。
- *
- * kind 常见值:
- * - SDI
- * - DAI
- */
-public class DoiElementNode {
- private String kind;
- private String name;
- private Long ix;
- private List values = new ArrayList();
- private List children = new ArrayList();
-
- public String getKind() { return kind; }
- public void setKind(String kind) { this.kind = kind; }
- public String getName() { return name; }
- public void setName(String name) { this.name = name; }
- public Long getIx() { return ix; }
- public void setIx(Long ix) { this.ix = ix; }
- public List getValues() { return values; }
- public void setValues(List values) { this.values = values; }
- public List getChildren() { return children; }
- public void setChildren(List children) { this.children = children; }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/DoiNode.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/DoiNode.java
deleted file mode 100644
index 51f7a37..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/DoiNode.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.icd;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * DOI 节点。
- * DOI 模型。表示逻辑节点下的一个数据对象节点。
- */
-public class DoiNode {
- private String name;
- private Long ix;
- private String lnClass;
- private String lnInst;
- private int sequenceCount;
- private List children = new ArrayList();
-
- public String getName() { return name; }
- public void setName(String name) { this.name = name; }
- public Long getIx() { return ix; }
- public void setIx(Long ix) { this.ix = ix; }
- public String getLnClass() { return lnClass; }
- public void setLnClass(String lnClass) { this.lnClass = lnClass; }
- public String getLnInst() { return lnInst; }
- public void setLnInst(String lnInst) { this.lnInst = lnInst; }
- public int getSequenceCount() { return sequenceCount; }
- public void setSequenceCount(int sequenceCount) { this.sequenceCount = sequenceCount; }
- public List getChildren() { return children; }
- public void setChildren(List children) { this.children = children; }
-}
\ No newline at end of file
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/FcdaNode.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/FcdaNode.java
deleted file mode 100644
index db985bf..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/FcdaNode.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.icd;
-
-/**
- * FCDA 节点。
- * FCDA 模型。保存 lnClass、lnInst、doName、daName、fc、ix 等信息。
- */
-public class FcdaNode {
- private String ldInst;
- private String prefix;
- private String lnClass;
- private String lnInst;
- private String doName;
- private String daName;
- private String fc;
- private Long ix;
- private int sequenceCount;
-
- public String getLdInst() { return ldInst; }
- public void setLdInst(String ldInst) { this.ldInst = ldInst; }
- public String getPrefix() { return prefix; }
- public void setPrefix(String prefix) { this.prefix = prefix; }
- public String getLnClass() { return lnClass; }
- public void setLnClass(String lnClass) { this.lnClass = lnClass; }
- public String getLnInst() { return lnInst; }
- public void setLnInst(String lnInst) { this.lnInst = lnInst; }
- public String getDoName() { return doName; }
- public void setDoName(String doName) { this.doName = doName; }
- public String getDaName() { return daName; }
- public void setDaName(String daName) { this.daName = daName; }
- public String getFc() { return fc; }
- public void setFc(String fc) { this.fc = fc; }
- public Long getIx() { return ix; }
- public void setIx(Long ix) { this.ix = ix; }
- public int getSequenceCount() { return sequenceCount; }
- public void setSequenceCount(int sequenceCount) { this.sequenceCount = sequenceCount; }
-}
\ No newline at end of file
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/IcdDocument.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/IcdDocument.java
deleted file mode 100644
index d3aed97..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/IcdDocument.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.icd;
-
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * ICD 文档聚合根。
- * ICD 统一领域模型的聚合根。承接 IED、LDevice、ReportControl、DataSet、LN 等解析结果。
- *
- * 说明:
- * 1. 这是系统内部统一的 ICD 模型。
- * 2. 外部 JAXB generated 类只在 parser 层使用。
- * 3. 业务层全部依赖这个标准化模型,便于后续替换解析实现。
- */
-public class IcdDocument {
- private String fileName;
- private String iedName;
- private String ldInst;
- private String ldPrefix;
- private IedNode primaryIed;
- private LogicalDeviceNode logicalDevice;
- private List logicalNodes = new ArrayList();
- private List reportControls = new ArrayList();
- private Map dataSets = new LinkedHashMap();
-
- public String getFileName() { return fileName; }
- public void setFileName(String fileName) { this.fileName = fileName; }
- public String getIedName() { return iedName; }
- public void setIedName(String iedName) { this.iedName = iedName; }
- public String getLdInst() { return ldInst; }
- public void setLdInst(String ldInst) { this.ldInst = ldInst; }
- public String getLdPrefix() { return ldPrefix; }
- public void setLdPrefix(String ldPrefix) { this.ldPrefix = ldPrefix; }
- public IedNode getPrimaryIed() { return primaryIed; }
- public void setPrimaryIed(IedNode primaryIed) { this.primaryIed = primaryIed; }
- public LogicalDeviceNode getLogicalDevice() { return logicalDevice; }
- public void setLogicalDevice(LogicalDeviceNode logicalDevice) { this.logicalDevice = logicalDevice; }
- public List getLogicalNodes() { return logicalNodes; }
- public void setLogicalNodes(List logicalNodes) { this.logicalNodes = logicalNodes; }
- public List getReportControls() { return reportControls; }
- public void setReportControls(List reportControls) { this.reportControls = reportControls; }
- public Map getDataSets() { return dataSets; }
- public void setDataSets(Map dataSets) { this.dataSets = dataSets; }
-}
\ No newline at end of file
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/IedNode.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/IedNode.java
deleted file mode 100644
index efc1a35..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/IedNode.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.icd;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * IED 节点。
- * IED 节点模型。保存 IED 名称以及其下逻辑设备引用。
- */
-public class IedNode {
- private String name;
- private List accessPointNames = new ArrayList();
- private List lDeviceInstList = new ArrayList();
- public String getName() { return name; }
- public void setName(String name) { this.name = name; }
- public List getAccessPointNames() { return accessPointNames; }
- public void setAccessPointNames(List accessPointNames) { this.accessPointNames = accessPointNames; }
- public List getLDeviceInstList() { return lDeviceInstList; }
- public void setLDeviceInstList(List lDeviceInstList) { this.lDeviceInstList = lDeviceInstList; }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/LnNode.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/LnNode.java
deleted file mode 100644
index f106aa4..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/LnNode.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.icd;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 逻辑节点。
- * 逻辑节点模型。保存 lnClass、lnInst、prefix、DOI 等信息。
- *
- * LN0 和 LN 最终都统一抽象为这个模型。
- */
-public class LnNode {
- private boolean ln0;
- private String prefix;
- private String lnClass;
- private String lnInst;
- private String lnType;
- private List doiList = new ArrayList();
-
- public boolean isLn0() { return ln0; }
- public void setLn0(boolean ln0) { this.ln0 = ln0; }
- public String getPrefix() { return prefix; }
- public void setPrefix(String prefix) { this.prefix = prefix; }
- public String getLnClass() { return lnClass; }
- public void setLnClass(String lnClass) { this.lnClass = lnClass; }
- public String getLnInst() { return lnInst; }
- public void setLnInst(String lnInst) { this.lnInst = lnInst; }
- public String getLnType() { return lnType; }
- public void setLnType(String lnType) { this.lnType = lnType; }
- public List getDoiList() { return doiList; }
- public void setDoiList(List doiList) { this.doiList = doiList; }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/LogicalDeviceNode.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/LogicalDeviceNode.java
deleted file mode 100644
index 07a47c2..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/LogicalDeviceNode.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.icd;
-
-/**
- * 逻辑设备节点。
- * 逻辑设备模型。保存 inst、prefix 以及其下 LN/LN0 列表。
- */
-public class LogicalDeviceNode {
- private String inst;
- private String ldName;
- public String getInst() { return inst; }
- public void setInst(String inst) { this.inst = inst; }
- public String getLdName() { return ldName; }
- public void setLdName(String ldName) { this.ldName = ldName; }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/ReportControlNode.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/ReportControlNode.java
deleted file mode 100644
index 476295d..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/icd/ReportControlNode.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.icd;
-
-/**
- * ReportControl 节点。
- * 报告控制块模型。用于保存报告名称、关联 DataSet、缓冲属性等。
- */
-public class ReportControlNode {
- private String name;
- private String rptId;
- private boolean buffered;
- private String dataSetName;
- private String trgOps;
- private String confRev;
-
- public String getName() { return name; }
- public void setName(String name) { this.name = name; }
- public String getRptId() { return rptId; }
- public void setRptId(String rptId) { this.rptId = rptId; }
- public boolean isBuffered() { return buffered; }
- public void setBuffered(boolean buffered) { this.buffered = buffered; }
- public String getDataSetName() { return dataSetName; }
- public void setDataSetName(String dataSetName) { this.dataSetName = dataSetName; }
- public String getTrgOps() { return trgOps; }
- public void setTrgOps(String trgOps) { this.trgOps = trgOps; }
- public String getConfRev() { return confRev; }
- public void setConfRev(String confRev) { this.confRev = confRev; }
- public Boolean getBuffered() { return buffered; }
- public void setBuffered(Boolean buffered) { this.buffered = buffered; }
-}
\ No newline at end of file
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/intermediate/DataSetSelectionState.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/intermediate/DataSetSelectionState.java
deleted file mode 100644
index 14d8f84..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/intermediate/DataSetSelectionState.java
+++ /dev/null
@@ -1,103 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.intermediate;
-
-import com.njcn.gather.icd.mapping.domain.model.icd.LnNode;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 最终参与生成 DataSetList 的选择状态。
- *
- * 关键修正:
- * 旧版本一个绑定只保存一个 LnNode,导致:
- * - MSQI 整组丢失
- * - MHAI 只生成一半
- *
- * 新版本改成:
- * 一个绑定可以关联多个 LnNode,后续生成阶段再逐个展开。
- */
-public class DataSetSelectionState {
-
- /** 所属分组 key。 */
- private String groupKey;
-
- /** 所属分组 desc。 */
- private String groupDesc;
-
- /** 报告名。 */
- private String reportName;
-
- /** 数据集名。 */
- private String dataSetName;
-
- /** 绑定标签。 */
- private String label;
-
- /** 绑定的 lnInst。 */
- private String lnInst;
-
- /**
- * 当前绑定最终命中的 LN 节点列表。
- *
- * 说明:
- * 同一个 lnInst 在不同 lnClass 下可能都需要展开,
- * 例如:MMXU / MSQI / MHAI。
- */
- private List lnNodes = new ArrayList();
-
- public String getGroupKey() {
- return groupKey;
- }
-
- public void setGroupKey(String groupKey) {
- this.groupKey = groupKey;
- }
-
- public String getGroupDesc() {
- return groupDesc;
- }
-
- public void setGroupDesc(String groupDesc) {
- this.groupDesc = groupDesc;
- }
-
- public String getReportName() {
- return reportName;
- }
-
- public void setReportName(String reportName) {
- this.reportName = reportName;
- }
-
- public String getDataSetName() {
- return dataSetName;
- }
-
- public void setDataSetName(String dataSetName) {
- this.dataSetName = dataSetName;
- }
-
- public String getLabel() {
- return label;
- }
-
- public void setLabel(String label) {
- this.label = label;
- }
-
- public String getLnInst() {
- return lnInst;
- }
-
- public void setLnInst(String lnInst) {
- this.lnInst = lnInst;
- }
-
- public List getLnNodes() {
- return lnNodes;
- }
-
- public void setLnNodes(List lnNodes) {
- this.lnNodes = lnNodes;
- }
-}
\ No newline at end of file
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/intermediate/ReportAndDataSetState.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/intermediate/ReportAndDataSetState.java
deleted file mode 100644
index d4b7950..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/intermediate/ReportAndDataSetState.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.intermediate;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 中间态总对象。
- *
- * 作用:
- * 1. 对应原 C# 里“先形成中间态,再做最终 JSON 组装”的思路;
- * 2. 把业务分组、用户绑定、最终 DataSet 选择结果集中保存;
- * 3. 避免在 MappingGenerationService 里直接边遍历边拼 JSON,便于后续扩展和排查。
- */
-public class ReportAndDataSetState {
-
- /** IED 名称。 */
- private String iedName;
-
- /** LD 实例名。 */
- private String ldInst;
-
- /** 分组状态列表。 */
- private List reportGroups = new ArrayList();
-
- /** 数据集选择状态列表。 */
- private List dataSetSelections = new ArrayList();
-
- public String getIedName() {
- return iedName;
- }
-
- public void setIedName(String iedName) {
- this.iedName = iedName;
- }
-
- public String getLdInst() {
- return ldInst;
- }
-
- public void setLdInst(String ldInst) {
- this.ldInst = ldInst;
- }
-
- public List getReportGroups() {
- return reportGroups;
- }
-
- public void setReportGroups(List reportGroups) {
- this.reportGroups = reportGroups;
- }
-
- public List getDataSetSelections() {
- return dataSetSelections;
- }
-
- public void setDataSetSelections(List dataSetSelections) {
- this.dataSetSelections = dataSetSelections;
- }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/intermediate/ReportBindingState.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/intermediate/ReportBindingState.java
deleted file mode 100644
index e433073..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/intermediate/ReportBindingState.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.intermediate;
-
-/**
- * 单条最终有效绑定关系的中间态。
- */
-public class ReportBindingState {
-
- /** 分组 key。 */
- private String groupKey;
-
- /** 分组描述。 */
- private String groupDesc;
-
- /** 报告名。 */
- private String reportName;
-
- /** 数据集名。 */
- private String dataSetName;
-
- /** 标签。 */
- private String label;
-
- /** 绑定的 lnInst 数字。 */
- private String lnInst;
-
- public String getGroupKey() {
- return groupKey;
- }
-
- public void setGroupKey(String groupKey) {
- this.groupKey = groupKey;
- }
-
- public String getGroupDesc() {
- return groupDesc;
- }
-
- public void setGroupDesc(String groupDesc) {
- this.groupDesc = groupDesc;
- }
-
- public String getReportName() {
- return reportName;
- }
-
- public void setReportName(String reportName) {
- this.reportName = reportName;
- }
-
- public String getDataSetName() {
- return dataSetName;
- }
-
- public void setDataSetName(String dataSetName) {
- this.dataSetName = dataSetName;
- }
-
- public String getLabel() {
- return label;
- }
-
- public void setLabel(String label) {
- this.label = label;
- }
-
- public String getLnInst() {
- return lnInst;
- }
-
- public void setLnInst(String lnInst) {
- this.lnInst = lnInst;
- }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/intermediate/ReportGroupState.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/intermediate/ReportGroupState.java
deleted file mode 100644
index 385c303..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/intermediate/ReportGroupState.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.intermediate;
-
-import com.njcn.gather.icd.mapping.domain.model.analysis.IndexCandidateReportItem;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 单个业务分组的中间态。
- */
-public class ReportGroupState {
-
- /** 分组唯一键。 */
- private String groupKey;
-
- /** 分组描述。 */
- private String groupDesc;
-
- /** 当前分组匹配到的报告数量。 */
- private int reportCount;
-
- /** 报表 inst(来自 DefaultCfg.ReportList.inst)。 */
- private String reportInst;
-
- /** Select(来自 DefaultCfg.ReportList.Select)。 */
- private String select;
-
- /** TrgOps(来自 DefaultCfg.ReportList.TrgOps)。 */
- private String trgOps;
-
- /** 当前分组包含的报告列表。 */
- private List reportItems = new ArrayList();
-
- /** 用户最终确认的绑定关系。 */
- private List bindings = new ArrayList();
-
- public String getGroupKey() {
- return groupKey;
- }
-
- public void setGroupKey(String groupKey) {
- this.groupKey = groupKey;
- }
-
- public String getGroupDesc() {
- return groupDesc;
- }
-
- public void setGroupDesc(String groupDesc) {
- this.groupDesc = groupDesc;
- }
-
- public int getReportCount() {
- return reportCount;
- }
-
- public void setReportCount(int reportCount) {
- this.reportCount = reportCount;
- }
-
- public String getReportInst() {
- return reportInst;
- }
-
- public void setReportInst(String reportInst) {
- this.reportInst = reportInst;
- }
-
- public String getSelect() {
- return select;
- }
-
- public void setSelect(String select) {
- this.select = select;
- }
-
- public String getTrgOps() {
- return trgOps;
- }
-
- public void setTrgOps(String trgOps) {
- this.trgOps = trgOps;
- }
-
- public List getReportItems() {
- return reportItems;
- }
-
- public void setReportItems(List reportItems) {
- this.reportItems = reportItems;
- }
-
- public List getBindings() {
- return bindings;
- }
-
- public void setBindings(List bindings) {
- this.bindings = bindings;
- }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/DataSetGroupItem.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/DataSetGroupItem.java
deleted file mode 100644
index e6cf35f..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/DataSetGroupItem.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.mapping;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 最终映射中的 DataSetList 单项。
- */
-public class DataSetGroupItem {
-
- /** 分组描述。一般来自 LnClassList.desc。 */
- private String desc;
-
- /** lnClass。 */
- private String lnClass;
-
- /** 该 lnClass 下的 inst 列表。 */
- private List instList = new ArrayList();
-
- public String getDesc() {
- return desc;
- }
-
- public void setDesc(String desc) {
- this.desc = desc;
- }
-
- public String getLnClass() {
- return lnClass;
- }
-
- public void setLnClass(String lnClass) {
- this.lnClass = lnClass;
- }
-
- public List getInstList() {
- return instList;
- }
-
- public void setInstList(List instList) {
- this.instList = instList;
- }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/DoiItem.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/DoiItem.java
deleted file mode 100644
index 1aa5f35..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/DoiItem.java
+++ /dev/null
@@ -1,120 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.mapping;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 最终映射中的 doiList 单项。
- */
-public class DoiItem {
-
- /** DOI 名称。 */
- private String name;
-
- /** DOI 描述。 */
- private String desc;
-
- /** 起始序号。 */
- private int start;
-
- /** 结束序号。 */
- private int end;
-
- /** 单位。 */
- private String unit;
-
- /** 系数。 */
- private float coefficient;
-
- /** 基波标志。 */
- private int baseflag;
-
- /** 基波数量。 */
- private int basecount;
-
- /** ICD 实际序列数。 */
- private int icdcout;
-
- /** SDI 列表。 */
- private List sdiList = new ArrayList();
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getDesc() {
- return desc;
- }
-
- public void setDesc(String desc) {
- this.desc = desc;
- }
-
- public int getStart() {
- return start;
- }
-
- public void setStart(int start) {
- this.start = start;
- }
-
- public int getEnd() {
- return end;
- }
-
- public void setEnd(int end) {
- this.end = end;
- }
-
- public String getUnit() {
- return unit;
- }
-
- public void setUnit(String unit) {
- this.unit = unit;
- }
-
- public float getCoefficient() {
- return coefficient;
- }
-
- public void setCoefficient(float coefficient) {
- this.coefficient = coefficient;
- }
-
- public int getBaseflag() {
- return baseflag;
- }
-
- public void setBaseflag(int baseflag) {
- this.baseflag = baseflag;
- }
-
- public int getBasecount() {
- return basecount;
- }
-
- public void setBasecount(int basecount) {
- this.basecount = basecount;
- }
-
- public int getIcdcout() {
- return icdcout;
- }
-
- public void setIcdcout(int icdcout) {
- this.icdcout = icdcout;
- }
-
- public List getSdiList() {
- return sdiList;
- }
-
- public void setSdiList(List sdiList) {
- this.sdiList = sdiList;
- }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/InstItem.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/InstItem.java
deleted file mode 100644
index 5e30e02..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/InstItem.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.mapping;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 最终映射中的 instList 单项。
- */
-public class InstItem {
-
- /** lnInst。 */
- private String inst;
-
- /** 该 inst 的描述。通常使用当前绑定的 label。 */
- private String desc;
-
- /** DOI 列表。 */
- private List doiList = new ArrayList();
-
- public String getInst() {
- return inst;
- }
-
- public void setInst(String inst) {
- this.inst = inst;
- }
-
- public String getDesc() {
- return desc;
- }
-
- public void setDesc(String desc) {
- this.desc = desc;
- }
-
- public List getDoiList() {
- return doiList;
- }
-
- public void setDoiList(List doiList) {
- this.doiList = doiList;
- }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/MappingDocument.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/MappingDocument.java
deleted file mode 100644
index a029389..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/MappingDocument.java
+++ /dev/null
@@ -1,127 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.mapping;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 最终映射文档。
- *
- * 关键说明:
- * 1. Java 字段统一使用 lowerCamelCase,避免 Jackson 同时输出 ied/IED 这类重复字段。
- * 2. JSON 输出名通过 @JsonProperty 显式指定,确保与原 C# 输出格式一致。
- */
-public class MappingDocument {
-
- @JsonProperty("version")
- private String version;
-
- @JsonProperty("author")
- private String author;
-
- @JsonProperty("IED")
- private String ied;
-
- @JsonProperty("LD")
- private String ld;
-
- /**
- * 原 C# mainFrom.txt 固定调用:
- * Set_WaveTimeFlag("BeiJing")
- */
- @JsonProperty("WaveTimeFlag")
- private String waveTimeFlag;
-
- /**
- * 原 C# mainFrom.txt 固定调用:
- * Set_DataType("1")
- */
- @JsonProperty("DataType")
- private String dataType;
-
- /**
- * 原 C# mainFrom.txt 固定调用:
- * Set_unit("1")
- */
- @JsonProperty("unit")
- private String unit;
-
- @JsonProperty("ReportMap")
- private List reportMap = new ArrayList();
-
- @JsonProperty("DataSetList")
- private List dataSetList = new ArrayList();
-
- public String getVersion() {
- return version;
- }
-
- public void setVersion(String version) {
- this.version = version;
- }
-
- public String getAuthor() {
- return author;
- }
-
- public void setAuthor(String author) {
- this.author = author;
- }
-
- public String getIed() {
- return ied;
- }
-
- public void setIed(String ied) {
- this.ied = ied;
- }
-
- public String getLd() {
- return ld;
- }
-
- public void setLd(String ld) {
- this.ld = ld;
- }
-
- public String getWaveTimeFlag() {
- return waveTimeFlag;
- }
-
- public void setWaveTimeFlag(String waveTimeFlag) {
- this.waveTimeFlag = waveTimeFlag;
- }
-
- public String getDataType() {
- return dataType;
- }
-
- public void setDataType(String dataType) {
- this.dataType = dataType;
- }
-
- public String getUnit() {
- return unit;
- }
-
- public void setUnit(String unit) {
- this.unit = unit;
- }
-
- public List getReportMap() {
- return reportMap;
- }
-
- public void setReportMap(List reportMap) {
- this.reportMap = reportMap;
- }
-
- public List getDataSetList() {
- return dataSetList;
- }
-
- public void setDataSetList(List dataSetList) {
- this.dataSetList = dataSetList;
- }
-}
\ No newline at end of file
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/ReportMapItem.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/ReportMapItem.java
deleted file mode 100644
index 0063d7b..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/ReportMapItem.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.mapping;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-/**
- * 最终映射中的 ReportMap 单项。
- *
- * 关键说明:
- * 1. 原 C# 不是把同组报告合并成一条,而是组内每个报告各自输出一条。
- * 2. 但 reportCount 仍然写该分组总数。
- * 3. buffered 不是 boolean,而是:
- * - true -> BR
- * - false -> RP
- */
-public class ReportMapItem {
-
- @JsonProperty("desc")
- private String desc;
-
- @JsonProperty("reportCount")
- private int reportCount;
-
- @JsonProperty("rptID")
- private String rptId;
-
- @JsonProperty("name")
- private String name;
-
- @JsonProperty("buffered")
- private String buffered;
-
- @JsonProperty("inst")
- private String inst;
-
- /**
- * 原 C# Set_FlickerFlag() 当前固定写 "0"
- */
- @JsonProperty("FlickerFlag")
- private String flickerFlag;
-
- /**
- * 原 C# 来自 DefaultCfg.ReportList.Select
- */
- @JsonProperty("Select")
- private String select;
-
- /**
- * 原 C# 来自 DefaultCfg.ReportList.TrgOps
- */
- @JsonProperty("TrgOps")
- private String trgOps;
-
- public String getDesc() {
- return desc;
- }
-
- public void setDesc(String desc) {
- this.desc = desc;
- }
-
- public int getReportCount() {
- return reportCount;
- }
-
- public void setReportCount(int reportCount) {
- this.reportCount = reportCount;
- }
-
- public String getRptId() {
- return rptId;
- }
-
- public void setRptId(String rptId) {
- this.rptId = rptId;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getBuffered() {
- return buffered;
- }
-
- public void setBuffered(String buffered) {
- this.buffered = buffered;
- }
-
- public String getInst() {
- return inst;
- }
-
- public void setInst(String inst) {
- this.inst = inst;
- }
-
- public String getFlickerFlag() {
- return flickerFlag;
- }
-
- public void setFlickerFlag(String flickerFlag) {
- this.flickerFlag = flickerFlag;
- }
-
- public String getSelect() {
- return select;
- }
-
- public void setSelect(String select) {
- this.select = select;
- }
-
- public String getTrgOps() {
- return trgOps;
- }
-
- public void setTrgOps(String trgOps) {
- this.trgOps = trgOps;
- }
-}
\ No newline at end of file
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/SdiItem.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/SdiItem.java
deleted file mode 100644
index 71c5aae..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/SdiItem.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.mapping;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * 最终映射中的 sdiList 单项。
- */
-public class SdiItem {
-
- /** SDI 名称。 */
- private String name;
-
- /** SDI 描述。 */
- private String desc;
-
- /** 类型列表。 */
- private List typeList = new ArrayList();
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getDesc() {
- return desc;
- }
-
- public void setDesc(String desc) {
- this.desc = desc;
- }
-
- public List getTypeList() {
- return typeList;
- }
-
- public void setTypeList(List typeList) {
- this.typeList = typeList;
- }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/TypeItem.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/TypeItem.java
deleted file mode 100644
index 81c133f..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/mapping/TypeItem.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.mapping;
-
-/**
- * 最终映射中的 typeList 单项。
- */
-public class TypeItem {
-
- /** 类型名称。 */
- private String name;
-
- /** 类型描述。 */
- private String desc;
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getDesc() {
- return desc;
- }
-
- public void setDesc(String desc) {
- this.desc = desc;
- }
-}
diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/template/DefaultTemplate.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/template/DefaultTemplate.java
deleted file mode 100644
index b369b60..0000000
--- a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/domain/model/template/DefaultTemplate.java
+++ /dev/null
@@ -1,345 +0,0 @@
-package com.njcn.gather.icd.mapping.domain.model.template;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonProperty;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-/**
- * 默认模板。
- * 默认模板模型。把 default-template.json 解析成可直接使用的对象。
- *
- * 当前只保留第一个接口真正会用到的部分。
- */
-@JsonIgnoreProperties(ignoreUnknown = true)
-public class DefaultTemplate {
-
- @JsonProperty("ReportList")
- private List reportList = new ArrayList();
-
- @JsonProperty("LnClassList")
- private List lnClassList = new ArrayList();
-
- @JsonProperty("PhaseList")
- private List phaseList = new ArrayList();
-
- @JsonProperty("MultiplierList")
- private List multiplierList = new ArrayList();
-
- @JsonProperty("UnitList")
- private List unitList = new ArrayList();
-
- @JsonProperty("TypeList")
- private List typeList = new ArrayList();
-
- @JsonProperty("DataObjectsList")
- private List dataObjectsList = new ArrayList();
-
- /**
- * 基础校验。
- *
- * 返回问题列表;为空表示通过。
- */
- public List verify() {
- List problems = new ArrayList();
- ensureDuplicateNames("LnClassList", extractNames(lnClassList), problems);
- ensureDuplicateNames("PhaseList", extractNames(phaseList), problems);
- ensureDuplicateNames("MultiplierList", extractNames(multiplierList), problems);
- ensureDuplicateNames("UnitList", extractNames(unitList), problems);
- ensureDuplicateNames("TypeList", extractNames(typeList), problems);
- ensureDuplicateObjectNames(problems);
- if (reportList == null || reportList.isEmpty()) {
- problems.add("DefaultCfg.ReportList 为空");
- }
- return problems;
- }
-
- private List extractNames(List extends NameListSupport> list) {
- List names = new ArrayList();
- if (list == null) {
- return names;
- }
- for (NameListSupport item : list) {
- if (item.getNameList() != null) {
- names.addAll(item.getNameList());
- }
- }
- return names;
- }
-
- private void ensureDuplicateNames(String section, List