feat(mms-mapping): 添加索引确认和选择构建功能

- 新增 buildIndexConfirmData 和 buildIndexSelection 两个调试接口
- 添加完整的请求参数和响应结果的数据结构定义
- 实现索引候选数据到确认模型的转换逻辑
- 实现确认结果到正式索引选择的展开生成功能
- 添加详细的 API 调试文档和使用示例
- 集成参数校验和业务规则验证机制
This commit is contained in:
2026-05-06 13:24:18 +08:00
parent f57fd45b47
commit 81e4ff4009
15 changed files with 1180 additions and 0 deletions

View File

@@ -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<T>` 标准包装。
- 业务异常和参数异常仍由全局异常处理器统一包装。
- 本次仅整理接口标准出口和调试文档,未改动索引组装算法。
- 本次未执行 `mvn` 编译、打包或真实接口联调。
## 2. 接口一buildIndexConfirmData
### 2.1 基本信息
| 项 | 说明 |
| --- | --- |
| 接口名称 | `buildIndexConfirmData` |
| 请求方法 | `POST` |
| 请求路径 | `/api/mms-mapping/build-index-confirm-data` |
| Content-Type | `application/json` |
| 控制器入口 | `MappingController#buildIndexConfirmData` |
| 成功响应体 | `HttpResult<List<IndexConfirmGroupResponse>>` |
### 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<List<IndexSelectionGroupResponse>>` |
### 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<String>`。常见场景包括:
- `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` 字段和值为结构化示意,实际成功码和提示文案以公共响应实现为准

View File

@@ -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<IndexBindingResponse> 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<IndexConfirmGroupResponse> buildConfirmData(List<IndexCandidateRequest> indexCandidates) {
if (indexCandidates == null || indexCandidates.isEmpty()) {
return Collections.emptyList();
}
List<IndexConfirmGroupResponse> result = new ArrayList<IndexConfirmGroupResponse>();
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<String> uniqueLabels = new LinkedHashSet<String>(candidate.getTemplateLabels());
for (String label : uniqueLabels) {
if (isBlank(label)) {
continue;
}
List<IndexCandidateReportItemRequest> targets = resolveTargets(candidate, label);
if (targets.isEmpty()) {
continue;
}
IndexConfirmLabelItemResponse labelItem = new IndexConfirmLabelItemResponse();
labelItem.setLabel(label);
labelItem.setRequired(false);
labelItem.setConfigurableOnce(true);
List<List<String>> targetLnInstLists = new ArrayList<List<String>>();
for (IndexCandidateReportItemRequest target : targets) {
IndexConfirmTargetResponse targetResponse = new IndexConfirmTargetResponse();
targetResponse.setReportName(target.getReportName());
targetResponse.setDataSetName(target.getDataSetName());
targetResponse.setReportDesc(target.getReportDesc());
List<String> labelSpecificValues = resolveAvailableLnInstValues(candidate, target, label);
targetResponse.getAvailableLnInstValues().addAll(labelSpecificValues);
targetLnInstLists.add(labelSpecificValues);
labelItem.getTargets().add(targetResponse);
}
List<String> 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<IndexSelectionGroupResponse> buildIndexSelection(BuildIndexSelectionRequest request) {
if (request == null) {
throw new IllegalArgumentException("请求体不能为空");
}
if (request.getConfirmData() == null || request.getConfirmData().isEmpty()) {
throw new IllegalArgumentException("confirmData 不能为空");
}
Map<String, IndexConfirmGroupRequest> confirmGroupMap = toConfirmGroupMap(request.getConfirmData());
Map<String, ConfirmedIndexGroupRequest> confirmedGroupMap = toConfirmedGroupMap(request.getConfirmedData());
validateConfirmedGroups(confirmGroupMap, confirmedGroupMap);
List<IndexSelectionGroupResponse> result = new ArrayList<IndexSelectionGroupResponse>();
for (IndexConfirmGroupRequest confirmGroup : request.getConfirmData()) {
if (confirmGroup == null || isBlank(confirmGroup.getGroupKey())) {
continue;
}
ConfirmedIndexGroupRequest confirmedGroup = confirmedGroupMap.get(confirmGroup.getGroupKey());
if (confirmedGroup == null) {
continue;
}
Map<String, ConfirmedLabelItemRequest> 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<String, IndexConfirmGroupRequest> confirmGroupMap,
Map<String, ConfirmedIndexGroupRequest> confirmedGroupMap) {
for (String groupKey : confirmedGroupMap.keySet()) {
if (!confirmGroupMap.containsKey(groupKey)) {
throw new IllegalArgumentException("confirmedData 中存在未知分组:" + groupKey);
}
}
}
private void validateConfirmedLabels(IndexConfirmGroupRequest confirmGroup,
Map<String, ConfirmedLabelItemRequest> confirmedLabelMap) {
if (confirmedLabelMap.isEmpty()) {
return;
}
Set<String> allowedLabels = new LinkedHashSet<String>();
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<String, IndexConfirmGroupRequest> toConfirmGroupMap(List<IndexConfirmGroupRequest> confirmData) {
Map<String, IndexConfirmGroupRequest> result = new LinkedHashMap<String, IndexConfirmGroupRequest>();
if (confirmData == null) {
return result;
}
for (IndexConfirmGroupRequest group : confirmData) {
if (group != null && !isBlank(group.getGroupKey())) {
result.put(group.getGroupKey(), group);
}
}
return result;
}
private Map<String, ConfirmedIndexGroupRequest> toConfirmedGroupMap(List<ConfirmedIndexGroupRequest> confirmedData) {
Map<String, ConfirmedIndexGroupRequest> result = new LinkedHashMap<String, ConfirmedIndexGroupRequest>();
if (confirmedData == null) {
return result;
}
for (ConfirmedIndexGroupRequest group : confirmedData) {
if (group != null && !isBlank(group.getGroupKey())) {
result.put(group.getGroupKey(), group);
}
}
return result;
}
private Map<String, ConfirmedLabelItemRequest> toConfirmedLabelMap(List<ConfirmedLabelItemRequest> labelItems) {
Map<String, ConfirmedLabelItemRequest> result = new LinkedHashMap<String, ConfirmedLabelItemRequest>();
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<IndexCandidateReportItemRequest> resolveTargets(IndexCandidateRequest candidate, String label) {
List<IndexCandidateReportItemRequest> reports = candidate.getReports();
if (reports == null || reports.isEmpty()) {
return Collections.emptyList();
}
boolean hasRealtimeInterReport = hasDataSet(reports, DATA_SET_REALTIME_INTER);
List<IndexCandidateReportItemRequest> result = new ArrayList<IndexCandidateReportItemRequest>();
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<IndexCandidateReportItemRequest> 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<String> intersectLnInstValues(List<List<String>> targetLnInstLists) {
if (targetLnInstLists == null || targetLnInstLists.isEmpty()) {
return Collections.emptyList();
}
List<String> firstValues = targetLnInstLists.get(0);
if (firstValues == null || firstValues.isEmpty()) {
return Collections.emptyList();
}
List<String> result = new ArrayList<String>();
for (String value : firstValues) {
if (isBlank(value)) {
continue;
}
boolean existsInAllTargets = true;
for (int i = 1; i < targetLnInstLists.size(); i++) {
List<String> currentValues = targetLnInstLists.get(i);
if (currentValues == null || !currentValues.contains(value)) {
existsInAllTargets = false;
break;
}
}
if (existsInAllTargets) {
result.add(value);
}
}
return result;
}
/**
* 某些未分组报告会把多种 label 的候选值拼在同一个 availableLnInstValues 里,
* 这里按当前业务规则先拆开后再返回给前端。
*/
private List<String> resolveAvailableLnInstValues(IndexCandidateRequest candidate,
IndexCandidateReportItemRequest target,
String label) {
if (target == null || target.getAvailableLnInstValues() == null) {
return Collections.emptyList();
}
List<String> 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<String>(values.subList(splitIndex, values.size()))
: new ArrayList<String>(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<String>(values.subList(1, values.size()))
: new ArrayList<String>(values.subList(0, 1));
}
return new ArrayList<String>(values);
}
private void validateConfirmedLabel(Map<String, IndexConfirmGroupRequest> 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();
}
}

View File

@@ -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<IndexConfirmGroupRequest> confirmData = new ArrayList<IndexConfirmGroupRequest>();
/** 前端最终确认后的选择结果。 */
@ApiModelProperty("前端最终确认后的选择结果")
private List<ConfirmedIndexGroupRequest> confirmedData = new ArrayList<ConfirmedIndexGroupRequest>();
}

View File

@@ -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<ConfirmedLabelItemRequest> labelItems = new ArrayList<ConfirmedLabelItemRequest>();
}

View File

@@ -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;
}

View File

@@ -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<String> availableLnInstValues = new ArrayList<String>();
}

View File

@@ -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<String> templateLabels = new ArrayList<String>();
/** 当前分组下的报告候选列表。 */
@ApiModelProperty("当前分组下的报告候选列表")
private List<IndexCandidateReportItemRequest> reports = new ArrayList<IndexCandidateReportItemRequest>();
}

View File

@@ -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<IndexConfirmLabelItemRequest> labelItems = new ArrayList<IndexConfirmLabelItemRequest>();
}

View File

@@ -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<String> commonLnInstValues = new ArrayList<String>();
/** 候选值唯一时的默认 lnInst。 */
@ApiModelProperty("候选值唯一时的默认 lnInst")
private String defaultLnInst;
/** 当前 label 影响的目标报告列表。 */
@ApiModelProperty("当前 label 影响的目标报告列表")
private List<IndexConfirmTargetRequest> targets = new ArrayList<IndexConfirmTargetRequest>();
}

View File

@@ -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<String> availableLnInstValues = new ArrayList<String>();
}

View File

@@ -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;
}

View File

@@ -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<IndexConfirmLabelItemResponse> labelItems = new ArrayList<IndexConfirmLabelItemResponse>();
}

View File

@@ -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<String> commonLnInstValues = new ArrayList<String>();
/** 候选值唯一时的默认 lnInst。 */
@ApiModelProperty("候选值唯一时的默认 lnInst")
private String defaultLnInst;
/** 当前 label 影响的全部目标报告。 */
@ApiModelProperty("当前 label 影响的全部目标报告")
private List<IndexConfirmTargetResponse> targets = new ArrayList<IndexConfirmTargetResponse>();
}

View File

@@ -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<String> availableLnInstValues = new ArrayList<String>();
}

View File

@@ -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<IndexBindingResponse> bindings = new ArrayList<IndexBindingResponse>();
}