17499cb526
- restartReviewWhenLastRejected 先删同一申报记录下旧节点再建第一级,同一事务 - 新增 ReviewFlowLevelValidator,按 title 校验层级从 1 连续升序 - ReviewFlowController 的 save/update/saveBatch/updateBatch 接入校验 - 单测覆盖驳回清理、层级缺级/重复/多链独立校验
82 lines
2.8 KiB
Java
82 lines
2.8 KiB
Java
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 连续升序)。
|
|
*
|
|
* <p>一条审批链的层级必须恰好是 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<ReviewFlow> flows) {
|
|
if (flows == null || flows.isEmpty()) {
|
|
return;
|
|
}
|
|
Map<String, List<Integer>> levelsByTitle = new LinkedHashMap<>();
|
|
for (ReviewFlow flow : flows) {
|
|
if (flow == null) {
|
|
continue;
|
|
}
|
|
levelsByTitle.computeIfAbsent(titleOf(flow), key -> new ArrayList<>()).add(flow.getLevel());
|
|
}
|
|
for (Map.Entry<String, List<Integer>> entry : levelsByTitle.entrySet()) {
|
|
validateOne(entry.getKey(), entry.getValue());
|
|
}
|
|
}
|
|
|
|
private static void validateOne(String title, List<Integer> levels) {
|
|
for (Integer level : levels) {
|
|
if (level == null) {
|
|
throw new BusinessException("审批链「" + displayTitle(title) + "」存在未填写层级的节点,层级必须从 1 连续升序");
|
|
}
|
|
}
|
|
List<Integer> 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<Integer> 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();
|
|
}
|
|
}
|