diff --git a/tools/mms-mapping/API-buildIndexDebug.md b/tools/mms-mapping/API-buildIndexDebug.md new file mode 100644 index 0000000..6978956 --- /dev/null +++ b/tools/mms-mapping/API-buildIndexDebug.md @@ -0,0 +1,308 @@ +# buildIndexConfirmData / buildIndexSelection 标准 API 调试文档 + +## 1. 文档范围 + +本文档用于说明 `mms-mapping` 模块中 `buildIndexConfirmData`、`buildIndexSelection` 两个调试接口的标准调用方式、请求结构、响应规则和联调注意事项。 + +本文档内容以当前源码为准,主要对照以下实现: + +- `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/IndexSelectionBuildService.java` +- `tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/IndexCandidateRequest.java` +- `tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/BuildIndexSelectionRequest.java` +- `tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IndexConfirmGroupResponse.java` +- `tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IndexSelectionGroupResponse.java` + +说明: + +- 两个接口当前统一返回 `HttpResult` 标准包装。 +- 业务异常和参数异常仍由全局异常处理器统一包装。 +- 本次仅整理接口标准出口和调试文档,未改动索引组装算法。 +- 本次未执行 `mvn` 编译、打包或真实接口联调。 + +## 2. 接口一:buildIndexConfirmData + +### 2.1 基本信息 + +| 项 | 说明 | +| --- | --- | +| 接口名称 | `buildIndexConfirmData` | +| 请求方法 | `POST` | +| 请求路径 | `/api/mms-mapping/build-index-confirm-data` | +| Content-Type | `application/json` | +| 控制器入口 | `MappingController#buildIndexConfirmData` | +| 成功响应体 | `HttpResult>` | + +### 2.2 接口职责 + +该接口用于把 `getIcdMmsJson` 或 `getICD` 返回的 `indexCandidates` 转换成前端可直接展示和确认的弹窗模型。 + +转换后的确认模型主要补充以下信息: + +- 每个分组下去重后的标签列表 +- 每个标签可配置的目标报告 +- 每个标签多个目标报告的 `lnInst` 共同候选值 +- 当共同候选值只有一个时,自动给出 `defaultLnInst` + +### 2.3 请求体 + +请求体为 `IndexCandidateRequest[]`,也就是候选分组数组。 + +示例: + +```json +[ + { + "groupKey": "统计数据__DSSTATISTICDATA", + "groupDesc": "统计数据", + "reportCount": 2, + "templateLabels": [ + "A相", + "B相", + "C相", + "间谐波1" + ], + "reports": [ + { + "reportName": "brcbStatistic", + "dataSetName": "dsStatisticData", + "reportDesc": "统计数据报告", + "availableLnInstValues": [ + "1", + "2", + "3", + "11", + "12", + "13" + ] + }, + { + "reportName": "brcbStatisticInter", + "dataSetName": "dsStIHarm", + "reportDesc": "间谐波统计报告", + "availableLnInstValues": [ + "11", + "12", + "13" + ] + } + ] + } +] +``` + +### 2.4 成功响应示例 + +```json +{ + "code": "200", + "msg": "buildIndexConfirmData,成功", + "data": [ + { + "groupKey": "统计数据__DSSTATISTICDATA", + "groupDesc": "统计数据", + "labelItems": [ + { + "label": "A相", + "required": false, + "configurableOnce": true, + "commonLnInstValues": [ + "1", + "2", + "3" + ], + "targets": [ + { + "reportName": "brcbStatistic", + "dataSetName": "dsStatisticData", + "reportDesc": "统计数据报告", + "availableLnInstValues": [ + "1", + "2", + "3" + ] + } + ] + } + ] + } + ] +} +``` + +说明: + +- `code`、`msg`、`data` 为标准 `HttpResult` 包装字段。 +- `data` 中的每个元素对应一个确认分组。 +- 若请求体为空数组,当前实现会返回成功响应,`data` 为空数组。 + +## 3. 接口二:buildIndexSelection + +### 3.1 基本信息 + +| 项 | 说明 | +| --- | --- | +| 接口名称 | `buildIndexSelection` | +| 请求方法 | `POST` | +| 请求路径 | `/api/mms-mapping/build-index-selection` | +| Content-Type | `application/json` | +| 控制器入口 | `MappingController#buildIndexSelection` | +| 成功响应体 | `HttpResult>` | + +### 3.2 接口职责 + +该接口用于根据前端确认模型 `confirmData` 和用户最终选择结果 `confirmedData`,展开生成正式的 `indexSelection` 结构,供 `getMmsJson` 或 `getIcdMmsJson` 第二次提交时直接复用。 + +该接口会在生成前做以下校验: + +- `request` 不能为空 +- `confirmData` 不能为空 +- `confirmedData` 中不能出现未知分组 +- 每个分组下不能出现未知标签 +- 已启用标签必须选择合法的 `lnInst` +- `lnInst` 必须同时落在该标签所有目标报告的允许范围内 + +### 3.3 请求体 + +请求体为 `BuildIndexSelectionRequest`,包含两部分: + +- `confirmData`:接口一返回并由前端回传的确认模型 +- `confirmedData`:用户在界面上实际确认后的启用状态和 `lnInst` + +示例: + +```json +{ + "confirmData": [ + { + "groupKey": "harm", + "groupDesc": "谐波数据", + "labelItems": [ + { + "label": "A相", + "required": false, + "configurableOnce": true, + "defaultLnInst": "1", + "commonLnInstValues": [ + "1", + "2", + "3" + ], + "targets": [ + { + "reportName": "brcbStHarm", + "dataSetName": "dsStHarm", + "reportDesc": "谐波报告", + "availableLnInstValues": [ + "1", + "2", + "3" + ] + } + ] + } + ] + } + ], + "confirmedData": [ + { + "groupKey": "harm", + "labelItems": [ + { + "label": "A相", + "enabled": true, + "lnInst": "1" + } + ] + } + ] +} +``` + +### 3.4 成功响应示例 + +```json +{ + "code": "200", + "msg": "buildIndexSelection,成功", + "data": [ + { + "groupKey": "harm", + "groupDesc": "谐波数据", + "bindings": [ + { + "reportName": "brcbStHarm", + "dataSetName": "dsStHarm", + "label": "A相", + "lnInst": "1" + } + ] + } + ] +} +``` + +说明: + +- 返回结果中的 `data` 就是可直接提交给 `getMmsJson` / `getIcdMmsJson` 的 `indexSelection`。 +- 只有启用且校验通过的标签才会被展开进最终 `bindings`。 + +### 3.5 失败响应说明 + +如果请求结构不合法或业务校验失败,会进入全局异常处理,返回统一 `HttpResult`。常见场景包括: + +- `request` 为空 +- `confirmData` 为空 +- `confirmedData` 中存在未知分组 +- 某个标签未选择 `lnInst` +- 选择的 `lnInst` 不在共同候选范围内 +- 选择的 `lnInst` 不在目标报告允许范围内 + +示例: + +```json +{ + "code": "500", + "msg": "buildIndexSelection,参数异常", + "data": "分组【谐波数据】的标签【A相】未选择 lnInst" +} +``` + +说明: + +- 这里的 `code`、`msg` 以公共异常包装实现为准,不保证与示例完全一致。 +- 具体错误文案由 `IndexSelectionBuildService` 抛出的 `IllegalArgumentException` 决定。 + +## 4. 标准调试顺序 + +推荐按以下顺序联调: + +1. 先调用 `getIcdMmsJson` 或 `getICD` 获取 `indexCandidates` +2. 把 `indexCandidates` 原样提交给 `buildIndexConfirmData` +3. 前端根据返回的确认模型组装 `confirmedData` +4. 调用 `buildIndexSelection` 生成正式 `indexSelection` +5. 把 `indexSelection` 提交给 `getMmsJson` 或 `getIcdMmsJson` 生成正式 `mappingJson` + +## 5. curl 调试示例 + +### 5.1 buildIndexConfirmData + +```powershell +curl.exe -X POST "http://localhost:8080/api/mms-mapping/build-index-confirm-data" ` + -H "Content-Type: application/json" ` + -d "[{\"groupKey\":\"harm\",\"groupDesc\":\"谐波数据\",\"reportCount\":1,\"templateLabels\":[\"A相\",\"B相\",\"C相\"],\"reports\":[{\"reportName\":\"brcbStHarm\",\"dataSetName\":\"dsStHarm\",\"reportDesc\":\"谐波报告\",\"availableLnInstValues\":[\"1\",\"2\",\"3\"]}]}]" +``` + +### 5.2 buildIndexSelection + +```powershell +curl.exe -X POST "http://localhost:8080/api/mms-mapping/build-index-selection" ` + -H "Content-Type: application/json" ` + -d "{\"confirmData\":[{\"groupKey\":\"harm\",\"groupDesc\":\"谐波数据\",\"labelItems\":[{\"label\":\"A相\",\"required\":false,\"configurableOnce\":true,\"defaultLnInst\":\"1\",\"commonLnInstValues\":[\"1\",\"2\",\"3\"],\"targets\":[{\"reportName\":\"brcbStHarm\",\"dataSetName\":\"dsStHarm\",\"reportDesc\":\"谐波报告\",\"availableLnInstValues\":[\"1\",\"2\",\"3\"]}]}]}],\"confirmedData\":[{\"groupKey\":\"harm\",\"labelItems\":[{\"label\":\"A相\",\"enabled\":true,\"lnInst\":\"1\"}]}]}" +``` + +## 6. 当前边界 + +- 本文档只覆盖 `buildIndexConfirmData` 和 `buildIndexSelection` +- `getIcdMmsJson` 独立调试方式仍以 `API-getIcdMmsJson.md` 为准 +- 示例中的 `HttpResult` 字段和值为结构化示意,实际成功码和提示文案以公共响应实现为准 diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/IndexSelectionBuildService.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/IndexSelectionBuildService.java new file mode 100644 index 0000000..f4cfb4e --- /dev/null +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/component/IndexSelectionBuildService.java @@ -0,0 +1,464 @@ +package com.njcn.gather.icd.mapping.component; + +import com.njcn.gather.icd.mapping.pojo.param.BuildIndexSelectionRequest; +import com.njcn.gather.icd.mapping.pojo.param.ConfirmedIndexGroupRequest; +import com.njcn.gather.icd.mapping.pojo.param.ConfirmedLabelItemRequest; +import com.njcn.gather.icd.mapping.pojo.param.IndexCandidateReportItemRequest; +import com.njcn.gather.icd.mapping.pojo.param.IndexCandidateRequest; +import com.njcn.gather.icd.mapping.pojo.param.IndexConfirmGroupRequest; +import com.njcn.gather.icd.mapping.pojo.param.IndexConfirmLabelItemRequest; +import com.njcn.gather.icd.mapping.pojo.param.IndexConfirmTargetRequest; +import com.njcn.gather.icd.mapping.pojo.vo.IndexBindingResponse; +import com.njcn.gather.icd.mapping.pojo.vo.IndexConfirmGroupResponse; +import com.njcn.gather.icd.mapping.pojo.vo.IndexConfirmLabelItemResponse; +import com.njcn.gather.icd.mapping.pojo.vo.IndexConfirmTargetResponse; +import com.njcn.gather.icd.mapping.pojo.vo.IndexSelectionGroupResponse; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * ICD 结构确认弹窗编排服务。 + * 第一阶段:把 indexCandidates 整理成前端可确认的 label 级模型。 + * 第二阶段:根据前端确认结果展开为最终 indexSelection。 + */ +@Service +public class IndexSelectionBuildService { + + private static final String GROUP_KEY_STATISTIC = "统计数据__DSSTATISTICDATA"; + private static final String GROUP_KEY_REALTIME = "实时数据__DSREALTIMEDATA"; + private static final String DATA_SET_STATISTIC_UNGROUPED = "dsStatisticData"; + private static final String DATA_SET_STATISTIC_INTER = "dsStIHarm"; + private static final String DATA_SET_REALTIME_UNGROUPED = "dsRealTimeData"; + private static final String DATA_SET_REALTIME_INTER = "dsRtIHarm"; + private static final String LABEL_REALTIME_INTER = "间谐波实时数据"; + + private static final Comparator INDEX_BINDING_COMPARATOR = (left, right) -> { + int result = normalizeSortValue(left == null ? null : left.getReportName()) + .compareTo(normalizeSortValue(right == null ? null : right.getReportName())); + if (result != 0) { + return result; + } + result = normalizeSortValue(left == null ? null : left.getDataSetName()) + .compareTo(normalizeSortValue(right == null ? null : right.getDataSetName())); + if (result != 0) { + return result; + } + return compareLnInstValues(left == null ? null : left.getLnInst(), right == null ? null : right.getLnInst()); + }; + + /** + * 根据页面回传的 indexCandidates 生成弹窗确认模型。 + */ + public List buildConfirmData(List indexCandidates) { + if (indexCandidates == null || indexCandidates.isEmpty()) { + return Collections.emptyList(); + } + + List result = new ArrayList(); + for (IndexCandidateRequest candidate : indexCandidates) { + if (candidate == null) { + continue; + } + + IndexConfirmGroupResponse groupResponse = new IndexConfirmGroupResponse(); + groupResponse.setGroupKey(candidate.getGroupKey()); + groupResponse.setGroupDesc(candidate.getGroupDesc()); + + if (candidate.getTemplateLabels() != null) { + Set uniqueLabels = new LinkedHashSet(candidate.getTemplateLabels()); + for (String label : uniqueLabels) { + if (isBlank(label)) { + continue; + } + + List targets = resolveTargets(candidate, label); + if (targets.isEmpty()) { + continue; + } + + IndexConfirmLabelItemResponse labelItem = new IndexConfirmLabelItemResponse(); + labelItem.setLabel(label); + labelItem.setRequired(false); + labelItem.setConfigurableOnce(true); + + List> targetLnInstLists = new ArrayList>(); + for (IndexCandidateReportItemRequest target : targets) { + IndexConfirmTargetResponse targetResponse = new IndexConfirmTargetResponse(); + targetResponse.setReportName(target.getReportName()); + targetResponse.setDataSetName(target.getDataSetName()); + targetResponse.setReportDesc(target.getReportDesc()); + List labelSpecificValues = resolveAvailableLnInstValues(candidate, target, label); + targetResponse.getAvailableLnInstValues().addAll(labelSpecificValues); + targetLnInstLists.add(labelSpecificValues); + labelItem.getTargets().add(targetResponse); + } + + List commonLnInstValues = intersectLnInstValues(targetLnInstLists); + labelItem.getCommonLnInstValues().addAll(commonLnInstValues); + if (commonLnInstValues.size() == 1) { + labelItem.setDefaultLnInst(commonLnInstValues.get(0)); + } + if (commonLnInstValues.isEmpty()) { + labelItem.setConfigurableOnce(false); + } + + groupResponse.getLabelItems().add(labelItem); + } + } + + result.add(groupResponse); + } + return result; + } + + /** + * 根据确认模型和前端最终确认结果展开生成 indexSelection。 + */ + public List buildIndexSelection(BuildIndexSelectionRequest request) { + if (request == null) { + throw new IllegalArgumentException("请求体不能为空"); + } + if (request.getConfirmData() == null || request.getConfirmData().isEmpty()) { + throw new IllegalArgumentException("confirmData 不能为空"); + } + + Map confirmGroupMap = toConfirmGroupMap(request.getConfirmData()); + Map confirmedGroupMap = toConfirmedGroupMap(request.getConfirmedData()); + validateConfirmedGroups(confirmGroupMap, confirmedGroupMap); + + List result = new ArrayList(); + for (IndexConfirmGroupRequest confirmGroup : request.getConfirmData()) { + if (confirmGroup == null || isBlank(confirmGroup.getGroupKey())) { + continue; + } + + ConfirmedIndexGroupRequest confirmedGroup = confirmedGroupMap.get(confirmGroup.getGroupKey()); + if (confirmedGroup == null) { + continue; + } + + Map confirmedLabelMap = toConfirmedLabelMap(confirmedGroup.getLabelItems()); + validateConfirmedLabels(confirmGroup, confirmedLabelMap); + IndexSelectionGroupResponse selectionGroup = new IndexSelectionGroupResponse(); + selectionGroup.setGroupKey(confirmGroup.getGroupKey()); + selectionGroup.setGroupDesc(confirmGroup.getGroupDesc()); + + if (confirmGroup.getLabelItems() != null) { + for (IndexConfirmLabelItemRequest confirmLabel : confirmGroup.getLabelItems()) { + if (confirmLabel == null || isBlank(confirmLabel.getLabel())) { + continue; + } + + ConfirmedLabelItemRequest confirmedLabel = confirmedLabelMap.get(confirmLabel.getLabel()); + if (confirmedLabel == null || !confirmedLabel.isEnabled()) { + continue; + } + + validateConfirmedLabel(confirmGroupMap, confirmGroup, confirmLabel, confirmedLabel); + expandBindings(selectionGroup, confirmLabel, confirmedLabel.getLnInst()); + } + } + + if (!selectionGroup.getBindings().isEmpty()) { + // 返回前统一排序,避免前端展示顺序受展开过程影响。 + selectionGroup.getBindings().sort(INDEX_BINDING_COMPARATOR); + result.add(selectionGroup); + } + } + return result; + } + + private void validateConfirmedGroups(Map confirmGroupMap, + Map confirmedGroupMap) { + for (String groupKey : confirmedGroupMap.keySet()) { + if (!confirmGroupMap.containsKey(groupKey)) { + throw new IllegalArgumentException("confirmedData 中存在未知分组:" + groupKey); + } + } + } + + private void validateConfirmedLabels(IndexConfirmGroupRequest confirmGroup, + Map confirmedLabelMap) { + if (confirmedLabelMap.isEmpty()) { + return; + } + Set allowedLabels = new LinkedHashSet(); + if (confirmGroup.getLabelItems() != null) { + for (IndexConfirmLabelItemRequest labelItem : confirmGroup.getLabelItems()) { + if (labelItem != null && !isBlank(labelItem.getLabel())) { + allowedLabels.add(labelItem.getLabel()); + } + } + } + for (String label : confirmedLabelMap.keySet()) { + if (!allowedLabels.contains(label)) { + throw new IllegalArgumentException("分组【" + confirmGroup.getGroupDesc() + "】中存在未知标签:" + label); + } + } + } + + private Map toConfirmGroupMap(List confirmData) { + Map result = new LinkedHashMap(); + if (confirmData == null) { + return result; + } + for (IndexConfirmGroupRequest group : confirmData) { + if (group != null && !isBlank(group.getGroupKey())) { + result.put(group.getGroupKey(), group); + } + } + return result; + } + + private Map toConfirmedGroupMap(List confirmedData) { + Map result = new LinkedHashMap(); + if (confirmedData == null) { + return result; + } + for (ConfirmedIndexGroupRequest group : confirmedData) { + if (group != null && !isBlank(group.getGroupKey())) { + result.put(group.getGroupKey(), group); + } + } + return result; + } + + private Map toConfirmedLabelMap(List labelItems) { + Map result = new LinkedHashMap(); + if (labelItems == null) { + return result; + } + for (ConfirmedLabelItemRequest item : labelItems) { + if (item != null && !isBlank(item.getLabel())) { + result.put(item.getLabel(), item); + } + } + return result; + } + + /** + * 统计数据、实时数据存在“同一 label 只配一次”的特殊归并规则, + * 其他分组默认把同名 label 展开到当前分组下全部报告。 + */ + private List resolveTargets(IndexCandidateRequest candidate, String label) { + List reports = candidate.getReports(); + if (reports == null || reports.isEmpty()) { + return Collections.emptyList(); + } + + boolean hasRealtimeInterReport = hasDataSet(reports, DATA_SET_REALTIME_INTER); + List result = new ArrayList(); + for (IndexCandidateReportItemRequest report : reports) { + if (report == null) { + continue; + } + + if (GROUP_KEY_STATISTIC.equals(candidate.getGroupKey())) { + if (isInterHarmonicLabel(label)) { + if (DATA_SET_STATISTIC_INTER.equals(report.getDataSetName()) || reports.size() == 1) { + result.add(report); + } + continue; + } + if (!DATA_SET_STATISTIC_INTER.equals(report.getDataSetName())) { + result.add(report); + } + continue; + } + + if (GROUP_KEY_REALTIME.equals(candidate.getGroupKey())) { + if (LABEL_REALTIME_INTER.equals(label)) { + if (DATA_SET_REALTIME_INTER.equals(report.getDataSetName())) { + result.add(report); + } else if (!hasRealtimeInterReport && DATA_SET_REALTIME_UNGROUPED.equals(report.getDataSetName())) { + result.add(report); + } + continue; + } + if (!DATA_SET_REALTIME_INTER.equals(report.getDataSetName())) { + result.add(report); + } + continue; + } + + result.add(report); + } + return result; + } + + private boolean hasDataSet(List reports, String dataSetName) { + if (reports == null || reports.isEmpty() || isBlank(dataSetName)) { + return false; + } + for (IndexCandidateReportItemRequest report : reports) { + if (report != null && dataSetName.equals(report.getDataSetName())) { + return true; + } + } + return false; + } + + /** + * 计算多个目标报告可共同接受的 lnInst 候选,保持首个目标报告的顺序。 + */ + private List intersectLnInstValues(List> targetLnInstLists) { + if (targetLnInstLists == null || targetLnInstLists.isEmpty()) { + return Collections.emptyList(); + } + + List firstValues = targetLnInstLists.get(0); + if (firstValues == null || firstValues.isEmpty()) { + return Collections.emptyList(); + } + + List result = new ArrayList(); + for (String value : firstValues) { + if (isBlank(value)) { + continue; + } + boolean existsInAllTargets = true; + for (int i = 1; i < targetLnInstLists.size(); i++) { + List currentValues = targetLnInstLists.get(i); + if (currentValues == null || !currentValues.contains(value)) { + existsInAllTargets = false; + break; + } + } + if (existsInAllTargets) { + result.add(value); + } + } + return result; + } + + /** + * 某些未分组报告会把多种 label 的候选值拼在同一个 availableLnInstValues 里, + * 这里按当前业务规则先拆开后再返回给前端。 + */ + private List resolveAvailableLnInstValues(IndexCandidateRequest candidate, + IndexCandidateReportItemRequest target, + String label) { + if (target == null || target.getAvailableLnInstValues() == null) { + return Collections.emptyList(); + } + + List values = target.getAvailableLnInstValues(); + if (GROUP_KEY_STATISTIC.equals(candidate.getGroupKey()) + && DATA_SET_STATISTIC_UNGROUPED.equals(target.getDataSetName()) + && values.size() > 1) { + int splitIndex = values.size() / 2; + return isInterHarmonicLabel(label) + ? new ArrayList(values.subList(splitIndex, values.size())) + : new ArrayList(values.subList(0, splitIndex)); + } + + if (GROUP_KEY_REALTIME.equals(candidate.getGroupKey()) + && DATA_SET_REALTIME_UNGROUPED.equals(target.getDataSetName()) + && values.size() > 1) { + return LABEL_REALTIME_INTER.equals(label) + ? new ArrayList(values.subList(1, values.size())) + : new ArrayList(values.subList(0, 1)); + } + + return new ArrayList(values); + } + + private void validateConfirmedLabel(Map confirmGroupMap, + IndexConfirmGroupRequest confirmGroup, + IndexConfirmLabelItemRequest confirmLabel, + ConfirmedLabelItemRequest confirmedLabel) { + if (!confirmGroupMap.containsKey(confirmGroup.getGroupKey())) { + throw new IllegalArgumentException("未找到确认分组:" + confirmGroup.getGroupKey()); + } + if (isBlank(confirmedLabel.getLnInst())) { + throw new IllegalArgumentException("分组【" + confirmGroup.getGroupDesc() + "】的标签【" + + confirmLabel.getLabel() + "】未选择 lnInst"); + } + if (confirmLabel.getCommonLnInstValues() == null || !confirmLabel.getCommonLnInstValues().contains(confirmedLabel.getLnInst())) { + throw new IllegalArgumentException( + "分组【" + confirmGroup.getGroupDesc() + "】的标签【" + confirmLabel.getLabel() + + "】选择的 lnInst【" + confirmedLabel.getLnInst() + "】不在共同候选范围内:" + + confirmLabel.getCommonLnInstValues() + ); + } + if (confirmLabel.getTargets() != null) { + for (IndexConfirmTargetRequest target : confirmLabel.getTargets()) { + if (target == null || target.getAvailableLnInstValues() == null) { + continue; + } + if (!target.getAvailableLnInstValues().contains(confirmedLabel.getLnInst())) { + throw new IllegalArgumentException( + "标签【" + confirmLabel.getLabel() + "】在目标报告【" + target.getReportName() + + "】下不支持 lnInst【" + confirmedLabel.getLnInst() + "】" + ); + } + } + } + } + + private void expandBindings(IndexSelectionGroupResponse selectionGroup, + IndexConfirmLabelItemRequest confirmLabel, + String lnInst) { + if (confirmLabel.getTargets() == null) { + return; + } + for (IndexConfirmTargetRequest target : confirmLabel.getTargets()) { + if (target == null) { + continue; + } + IndexBindingResponse binding = new IndexBindingResponse(); + binding.setReportName(target.getReportName()); + binding.setDataSetName(target.getDataSetName()); + binding.setLabel(confirmLabel.getLabel()); + binding.setLnInst(lnInst); + selectionGroup.getBindings().add(binding); + } + } + + private boolean isInterHarmonicLabel(String label) { + return label != null && label.startsWith("间谐波"); + } + + private static String normalizeSortValue(String value) { + return value == null ? "" : value; + } + + private static int compareLnInstValues(String left, String right) { + Long leftNumeric = parseNumericLnInst(left); + Long rightNumeric = parseNumericLnInst(right); + if (leftNumeric != null && rightNumeric != null) { + int result = leftNumeric.compareTo(rightNumeric); + if (result != 0) { + return result; + } + } + return normalizeSortValue(left).compareTo(normalizeSortValue(right)); + } + + private static Long parseNumericLnInst(String value) { + if (value == null) { + return null; + } + String trimmedValue = value.trim(); + if (trimmedValue.isEmpty()) { + return null; + } + try { + return Long.valueOf(trimmedValue); + } catch (NumberFormatException ex) { + return null; + } + } + + private boolean isBlank(String value) { + return value == null || value.trim().isEmpty(); + } +} diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/BuildIndexSelectionRequest.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/BuildIndexSelectionRequest.java new file mode 100644 index 0000000..d65c0bd --- /dev/null +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/BuildIndexSelectionRequest.java @@ -0,0 +1,24 @@ +package com.njcn.gather.icd.mapping.pojo.param; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 根据确认结果生成 indexSelection 的请求参数。 + */ +@Data +@ApiModel("根据确认结果生成 indexSelection 的请求参数") +public class BuildIndexSelectionRequest { + + /** 接口返回的确认模型数据。 */ + @ApiModelProperty("接口返回的确认模型数据") + private List confirmData = new ArrayList(); + + /** 前端最终确认后的选择结果。 */ + @ApiModelProperty("前端最终确认后的选择结果") + private List confirmedData = new ArrayList(); +} diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/ConfirmedIndexGroupRequest.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/ConfirmedIndexGroupRequest.java new file mode 100644 index 0000000..d2762b6 --- /dev/null +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/ConfirmedIndexGroupRequest.java @@ -0,0 +1,24 @@ +package com.njcn.gather.icd.mapping.pojo.param; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 前端提交的确认分组结果。 + */ +@Data +@ApiModel("前端提交的确认分组结果") +public class ConfirmedIndexGroupRequest { + + /** 分组唯一键。 */ + @ApiModelProperty("分组唯一键") + private String groupKey; + + /** 当前分组下按 label 汇总后的确认项。 */ + @ApiModelProperty("当前分组下按 label 汇总后的确认项") + private List labelItems = new ArrayList(); +} diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/ConfirmedLabelItemRequest.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/ConfirmedLabelItemRequest.java new file mode 100644 index 0000000..ea54faa --- /dev/null +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/ConfirmedLabelItemRequest.java @@ -0,0 +1,25 @@ +package com.njcn.gather.icd.mapping.pojo.param; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 前端提交的单个 label 确认结果。 + */ +@Data +@ApiModel("前端提交的单个 label 确认结果") +public class ConfirmedLabelItemRequest { + + /** 模板标签。 */ + @ApiModelProperty("模板标签") + private String label; + + /** 是否启用当前配置。 */ + @ApiModelProperty("是否启用当前配置") + private boolean enabled; + + /** 当前 label 选中的 lnInst。 */ + @ApiModelProperty("当前 label 选中的 lnInst") + private String lnInst; +} diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/IndexCandidateReportItemRequest.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/IndexCandidateReportItemRequest.java new file mode 100644 index 0000000..ef61ca5 --- /dev/null +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/IndexCandidateReportItemRequest.java @@ -0,0 +1,32 @@ +package com.njcn.gather.icd.mapping.pojo.param; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 索引候选中的单个报告请求项。 + */ +@Data +@ApiModel("索引候选中的单个报告请求项") +public class IndexCandidateReportItemRequest { + + /** 报告名称。 */ + @ApiModelProperty("报告名称") + private String reportName; + + /** 数据集名称。 */ + @ApiModelProperty("数据集名称") + private String dataSetName; + + /** 报告描述。 */ + @ApiModelProperty("报告描述") + private String reportDesc; + + /** 当前报告允许选择的 lnInst 值列表。 */ + @ApiModelProperty("当前报告允许选择的 lnInst 值列表") + private List availableLnInstValues = new ArrayList(); +} diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/IndexCandidateRequest.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/IndexCandidateRequest.java new file mode 100644 index 0000000..30d5073 --- /dev/null +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/IndexCandidateRequest.java @@ -0,0 +1,36 @@ +package com.njcn.gather.icd.mapping.pojo.param; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 索引候选分组请求参数。 + */ +@Data +@ApiModel("索引候选分组请求参数") +public class IndexCandidateRequest { + + /** 分组唯一键。 */ + @ApiModelProperty("分组唯一键") + private String groupKey; + + /** 分组中文描述。 */ + @ApiModelProperty("分组中文描述") + private String groupDesc; + + /** 当前分组包含的报告数量。 */ + @ApiModelProperty("当前分组包含的报告数量") + private int reportCount; + + /** 当前分组展示的模板标签列表。 */ + @ApiModelProperty("当前分组展示的模板标签列表") + private List templateLabels = new ArrayList(); + + /** 当前分组下的报告候选列表。 */ + @ApiModelProperty("当前分组下的报告候选列表") + private List reports = new ArrayList(); +} diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/IndexConfirmGroupRequest.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/IndexConfirmGroupRequest.java new file mode 100644 index 0000000..9ac7791 --- /dev/null +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/IndexConfirmGroupRequest.java @@ -0,0 +1,28 @@ +package com.njcn.gather.icd.mapping.pojo.param; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 索引确认分组请求参数。 + */ +@Data +@ApiModel("索引确认分组请求参数") +public class IndexConfirmGroupRequest { + + /** 分组唯一键。 */ + @ApiModelProperty("分组唯一键") + private String groupKey; + + /** 分组中文描述。 */ + @ApiModelProperty("分组中文描述") + private String groupDesc; + + /** 当前分组下按 label 聚合后的确认项。 */ + @ApiModelProperty("当前分组下按 label 聚合后的确认项") + private List labelItems = new ArrayList(); +} diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/IndexConfirmLabelItemRequest.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/IndexConfirmLabelItemRequest.java new file mode 100644 index 0000000..4f5de62 --- /dev/null +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/IndexConfirmLabelItemRequest.java @@ -0,0 +1,40 @@ +package com.njcn.gather.icd.mapping.pojo.param; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 索引确认模型中的单个 label 请求项。 + */ +@Data +@ApiModel("索引确认模型中的单个 label 请求项") +public class IndexConfirmLabelItemRequest { + + /** 模板标签。 */ + @ApiModelProperty("模板标签") + private String label; + + /** 是否必配。 */ + @ApiModelProperty("是否必配") + private boolean required; + + /** 是否支持一次配置后展开到多个报告。 */ + @ApiModelProperty("是否支持一次配置后展开到多个报告") + private boolean configurableOnce; + + /** 所有目标报告共同支持的 lnInst 候选值。 */ + @ApiModelProperty("所有目标报告共同支持的 lnInst 候选值") + private List commonLnInstValues = new ArrayList(); + + /** 候选值唯一时的默认 lnInst。 */ + @ApiModelProperty("候选值唯一时的默认 lnInst") + private String defaultLnInst; + + /** 当前 label 影响的目标报告列表。 */ + @ApiModelProperty("当前 label 影响的目标报告列表") + private List targets = new ArrayList(); +} diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/IndexConfirmTargetRequest.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/IndexConfirmTargetRequest.java new file mode 100644 index 0000000..e87e45e --- /dev/null +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/param/IndexConfirmTargetRequest.java @@ -0,0 +1,32 @@ +package com.njcn.gather.icd.mapping.pojo.param; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 索引确认模型中的目标报告请求项。 + */ +@Data +@ApiModel("索引确认模型中的目标报告请求项") +public class IndexConfirmTargetRequest { + + /** 报告名称。 */ + @ApiModelProperty("报告名称") + private String reportName; + + /** 数据集名称。 */ + @ApiModelProperty("数据集名称") + private String dataSetName; + + /** 报告描述。 */ + @ApiModelProperty("报告描述") + private String reportDesc; + + /** 当前目标报告可接受的 lnInst 列表。 */ + @ApiModelProperty("当前目标报告可接受的 lnInst 列表") + private List availableLnInstValues = new ArrayList(); +} diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IndexBindingResponse.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IndexBindingResponse.java new file mode 100644 index 0000000..50a10fd --- /dev/null +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IndexBindingResponse.java @@ -0,0 +1,31 @@ +package com.njcn.gather.icd.mapping.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 单条索引绑定响应项。 + */ +@Data +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@ApiModel("单条索引绑定响应项") +public class IndexBindingResponse { + + /** 报告名称。 */ + @ApiModelProperty("报告名称") + private String reportName; + + /** 数据集名称。 */ + @ApiModelProperty("数据集名称") + private String dataSetName; + + /** 模板标签。 */ + @ApiModelProperty("模板标签") + private String label; + + /** 当前标签对应的 lnInst。 */ + @ApiModelProperty("当前标签对应的 lnInst") + private String lnInst; +} diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IndexConfirmGroupResponse.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IndexConfirmGroupResponse.java new file mode 100644 index 0000000..1f71eb6 --- /dev/null +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IndexConfirmGroupResponse.java @@ -0,0 +1,30 @@ +package com.njcn.gather.icd.mapping.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 索引确认分组响应。 + */ +@Data +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@ApiModel("索引确认分组响应") +public class IndexConfirmGroupResponse { + + /** 分组唯一键。 */ + @ApiModelProperty("分组唯一键") + private String groupKey; + + /** 分组中文描述。 */ + @ApiModelProperty("分组中文描述") + private String groupDesc; + + /** 当前分组下按 label 聚合后的确认项。 */ + @ApiModelProperty("当前分组下按 label 聚合后的确认项") + private List labelItems = new ArrayList(); +} diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IndexConfirmLabelItemResponse.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IndexConfirmLabelItemResponse.java new file mode 100644 index 0000000..c3d7bb5 --- /dev/null +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IndexConfirmLabelItemResponse.java @@ -0,0 +1,42 @@ +package com.njcn.gather.icd.mapping.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 索引确认模型中的单个 label 响应项。 + */ +@Data +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@ApiModel("索引确认模型中的单个 label 响应项") +public class IndexConfirmLabelItemResponse { + + /** 模板标签。 */ + @ApiModelProperty("模板标签") + private String label; + + /** 是否必配。 */ + @ApiModelProperty("是否必配") + private boolean required; + + /** 是否支持一次配置后展开到多个报告。 */ + @ApiModelProperty("是否支持一次配置后展开到多个报告") + private boolean configurableOnce; + + /** 所有目标报告共同支持的 lnInst 候选值。 */ + @ApiModelProperty("所有目标报告共同支持的 lnInst 候选值") + private List commonLnInstValues = new ArrayList(); + + /** 候选值唯一时的默认 lnInst。 */ + @ApiModelProperty("候选值唯一时的默认 lnInst") + private String defaultLnInst; + + /** 当前 label 影响的全部目标报告。 */ + @ApiModelProperty("当前 label 影响的全部目标报告") + private List targets = new ArrayList(); +} diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IndexConfirmTargetResponse.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IndexConfirmTargetResponse.java new file mode 100644 index 0000000..0e52935 --- /dev/null +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IndexConfirmTargetResponse.java @@ -0,0 +1,34 @@ +package com.njcn.gather.icd.mapping.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 索引确认模型中的目标报告响应项。 + */ +@Data +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@ApiModel("索引确认模型中的目标报告响应项") +public class IndexConfirmTargetResponse { + + /** 报告名称。 */ + @ApiModelProperty("报告名称") + private String reportName; + + /** 数据集名称。 */ + @ApiModelProperty("数据集名称") + private String dataSetName; + + /** 报告描述。 */ + @ApiModelProperty("报告描述") + private String reportDesc; + + /** 当前目标报告可接受的 lnInst 列表。 */ + @ApiModelProperty("当前目标报告可接受的 lnInst 列表") + private List availableLnInstValues = new ArrayList(); +} diff --git a/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IndexSelectionGroupResponse.java b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IndexSelectionGroupResponse.java new file mode 100644 index 0000000..851241f --- /dev/null +++ b/tools/mms-mapping/src/main/java/com/njcn/gather/icd/mapping/pojo/vo/IndexSelectionGroupResponse.java @@ -0,0 +1,30 @@ +package com.njcn.gather.icd.mapping.pojo.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 生成后的索引选择分组响应。 + */ +@Data +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@ApiModel("生成后的索引选择分组响应") +public class IndexSelectionGroupResponse { + + /** 分组唯一键。 */ + @ApiModelProperty("分组唯一键") + private String groupKey; + + /** 分组中文描述。 */ + @ApiModelProperty("分组中文描述") + private String groupDesc; + + /** 当前分组最终生成的绑定关系。 */ + @ApiModelProperty("当前分组最终生成的绑定关系") + private List bindings = new ArrayList(); +}