package com.gxwebsoft.gxmu.service;
import com.gxwebsoft.common.core.exception.BusinessException;
import com.gxwebsoft.gxmu.entity.ReviewFlow;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* 审批链层级连续性校验(spec #58 行为约定:层级从 1 连续升序)。
*
*
一条审批链的层级必须恰好是 1..N,缺级会让申报卡在半路,重复级会让两个人拿到同一条待办。
* 按 title 分组校验,纯函数,不依赖数据库。
*
* @author Codex
* @since 2026-11-03
*/
public final class ReviewFlowLevelValidator {
private ReviewFlowLevelValidator() {
}
/**
* 校验一组审批流节点的层级;不合法时抛出 {@link BusinessException}。
*
* @param flows 同一轮保存涉及的节点,可包含多条审批链(按 title 分组)
*/
public static void validate(List flows) {
if (flows == null || flows.isEmpty()) {
return;
}
Map> levelsByTitle = new LinkedHashMap<>();
for (ReviewFlow flow : flows) {
if (flow == null) {
continue;
}
levelsByTitle.computeIfAbsent(titleOf(flow), key -> new ArrayList<>()).add(flow.getLevel());
}
for (Map.Entry> entry : levelsByTitle.entrySet()) {
validateOne(entry.getKey(), entry.getValue());
}
}
private static void validateOne(String title, List levels) {
for (Integer level : levels) {
if (level == null) {
throw new BusinessException("审批链「" + displayTitle(title) + "」存在未填写层级的节点,层级必须从 1 连续升序");
}
}
List sorted = new ArrayList<>(levels);
Collections.sort(sorted);
for (int index = 0; index < sorted.size(); index++) {
if (sorted.get(index) != index + 1) {
throw new BusinessException("审批链「" + displayTitle(title) + "」的层级必须从 1 连续升序,当前层级为 "
+ join(sorted) + ",请调整后再保存");
}
}
}
private static String titleOf(ReviewFlow flow) {
return flow.getTitle() == null ? "" : flow.getTitle().trim();
}
private static String displayTitle(String title) {
return title.isEmpty() ? "未命名" : title;
}
private static String join(List levels) {
StringBuilder builder = new StringBuilder();
for (int index = 0; index < levels.size(); index++) {
if (index > 0) {
builder.append(", ");
}
builder.append(levels.get(index));
}
return builder.toString();
}
}