dev_1.0.1
luotaiqian 2026-07-16 15:17:12 +08:00
parent 76070ba58a
commit c187397c3e
1 changed files with 58 additions and 161 deletions

View File

@ -21,6 +21,7 @@ import org.springframework.util.CollectionUtils;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.*; import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.qinan.safetyeval.domain.exception.ErrorCode.PERSON_NOT_ENOUGH; import static org.qinan.safetyeval.domain.exception.ErrorCode.PERSON_NOT_ENOUGH;
@ -361,32 +362,33 @@ public class ComplianceCheckUtil {
return Pair.of(true, null); return Pair.of(true, null);
} }
// 1. 汇总每个专业能力code的要求人数 Map<String, Long> requiredMap = new HashMap<>();
Map<String, Integer> requiredMap = standardsDOList.stream() for (IndustryProfessionalStandardsDO standardsDO : standardsDOList) {
.collect(Collectors.toMap( requiredMap.put(standardsDO.getProfessionalCapabilityCode(), standardsDO.getPersonNum());
IndustryProfessionalStandardsDO::getProfessionalCapabilityCode, }
s -> s.getPersonNum() != null ? s.getPersonNum().intValue() : 0,
Integer::sum,
LinkedHashMap::new));
// 2. 获取本行业备案人员集合(确定参检人员范围)
Set<Long> filingPersonIds = industryProfessionalPersonCos.stream()
.map(IndustryProfessionalPersonCo::getSourcePersonnelId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
// 3. 构建人员→可担任专业能力code集合同一人可能具备多个专业能力 // 3. 构建人员→可担任专业能力code集合同一人可能具备多个专业能力
Map<Long, Set<String>> personCapabilityMap = new HashMap<>(); Map<Long, Set<String>> personCapabilityMap = new HashMap<>();
for (OrgPersonnelCertCapabilityComplianceCheckCO cert : certCapabilityList) { for (IndustryProfessionalPersonCo industryProfessionalPersonCo : industryProfessionalPersonCos) {
Long pid = cert.getPersonnelId(); Set<String> codeList = personCapabilityMap
if (pid != null && filingPersonIds.contains(pid) && cert.getProfessionalCapabilityCode() != null) { .getOrDefault(industryProfessionalPersonCo.getSourcePersonnelId(), new HashSet<>());
personCapabilityMap.computeIfAbsent(pid, k -> new HashSet<>()) codeList.add(industryProfessionalPersonCo.getProfessionalCapabilityCode());
.add(cert.getProfessionalCapabilityCode()); personCapabilityMap.put(industryProfessionalPersonCo.getSourcePersonnelId()
} , codeList);
} }
// 4. 一人一岗最优分配 for (OrgPersonnelCertCapabilityComplianceCheckCO industryProfessionalPersonCo : certCapabilityList) {
Map<String, Integer> assignedMap = assignPersonnelByMaxFlow(personCapabilityMap, requiredMap); Set<String> codeList = personCapabilityMap
.getOrDefault(industryProfessionalPersonCo.getPersonnelId(), new HashSet<>());
codeList.add(industryProfessionalPersonCo.getProfessionalCapabilityCode());
personCapabilityMap.put(industryProfessionalPersonCo.getPersonnelId(), codeList);
}
// 数据来源industryProfessionalPersonCos备案人员自带专业能力+ certCapabilityList证书专业能力
// 4. 贪心分配(一人一岗):按可选人数从少到多处理,优先满足最紧缺的专业能力
Map<String, Integer> assignedMap = assignPersonnelGreedy(personCapabilityMap, requiredMap);
// 5. 判断是否所有专业能力都达标,并生成失败消息 // 5. 判断是否所有专业能力都达标,并生成失败消息
boolean allMet = true; boolean allMet = true;
@ -394,9 +396,9 @@ public class ComplianceCheckUtil {
String industryName = industryEnum != null ? industryEnum.getValue() : standardsDOList.get(0).getIndustryCode(); String industryName = industryEnum != null ? industryEnum.getValue() : standardsDOList.get(0).getIndustryCode();
StringBuilder failureMsg = new StringBuilder(); StringBuilder failureMsg = new StringBuilder();
for (Map.Entry<String, Integer> entry : requiredMap.entrySet()) { for (Map.Entry<String, Long> entry : requiredMap.entrySet()) {
String capCode = entry.getKey(); String capCode = entry.getKey();
int required = entry.getValue(); long required = entry.getValue();
int assigned = assignedMap.getOrDefault(capCode, 0); int assigned = assignedMap.getOrDefault(capCode, 0);
if (assigned < required) { if (assigned < required) {
allMet = false; allMet = false;
@ -407,7 +409,7 @@ public class ComplianceCheckUtil {
.append(capName) .append(capName)
.append(" 需要") .append(" 需要")
.append(required) .append(required)
.append("人,因一人一岗最多可分配") .append("人,当前可用")
.append(assigned) .append(assigned)
.append("人;"); .append("人;");
} }
@ -416,157 +418,52 @@ public class ComplianceCheckUtil {
} }
/** /**
* / Dinic *
* <p> * <p>
* * 使
* "一人一岗" *
* <p> * <p>
* : source (cap=1) (cap=1) sink(cap=) * "只需判断能否达标"
* *
* @param personCapabilityMap code * @param personCapabilityMap code
* @param requiredMap code * @param requiredMap code
* @return code * @return code
*/ */
private static Map<String, Integer> assignPersonnelByMaxFlow(Map<Long, Set<String>> personCapabilityMap private static Map<String, Integer> assignPersonnelGreedy(Map<Long, Set<String>> personCapabilityMap
, Map<String, Integer> requiredMap) { , Map<String, Long> requiredMap) {
List<Long> personIds = new ArrayList<>(personCapabilityMap.keySet()); // 构建 专业能力code → 可用人员集合
List<String> capCodes = new ArrayList<>(requiredMap.keySet()); Map<String, Set<Long>> capabilityPersonMap = new HashMap<>();
Map<String, Integer> capIndex = new HashMap<>(); for (Map.Entry<Long, Set<String>> entry : personCapabilityMap.entrySet()) {
for (int j = 0; j < capCodes.size(); j++) { Long pid = entry.getKey();
capIndex.put(capCodes.get(j), j); for (String cap : entry.getValue()) {
if (requiredMap.containsKey(cap)) {
capabilityPersonMap.computeIfAbsent(cap, k -> new HashSet<>()).add(pid);
}
}
} }
int numPersons = personIds.size(); // 按可选人数从少到多排序,优先满足最紧缺的专业能力
int numCaps = capCodes.size(); List<String> capOrder = new ArrayList<>(requiredMap.keySet());
int source = 0; capOrder.sort(Comparator.comparingInt(c -> capabilityPersonMap.getOrDefault(c, Collections.emptySet()).size()));
int sink = numPersons + numCaps + 1;
MaxFlow mf = new MaxFlow(sink + 1);
// source → person (capacity 1: 每人最多分配一个岗位) Set<Long> usedPersonIds = new HashSet<>();
for (int i = 0; i < numPersons; i++) {
mf.addEdge(source, 1 + i, 1);
}
// person → capability (capacity 1: 该人可担任该专业能力)
for (int i = 0; i < numPersons; i++) {
Set<String> caps = personCapabilityMap.get(personIds.get(i));
if (caps != null) {
for (String cap : caps) {
Integer cj = capIndex.get(cap);
if (cj != null) {
mf.addEdge(1 + i, 1 + numPersons + cj, 1);
}
}
}
}
// capability → sink (capacity = 该专业能力要求人数)
int[] capEdgeIds = new int[numCaps];
for (int j = 0; j < numCaps; j++) {
capEdgeIds[j] = mf.addEdgeReturnId(1 + numPersons + j, sink, requiredMap.get(capCodes.get(j)));
}
// 求解最大流
mf.maxFlow(source, sink);
// 提取每个专业能力的实际分配人数
Map<String, Integer> assignedMap = new LinkedHashMap<>(); Map<String, Integer> assignedMap = new LinkedHashMap<>();
for (int j = 0; j < numCaps; j++) { for (String capCode : capOrder) {
assignedMap.put(capCodes.get(j), mf.getFlow(capEdgeIds[j])); long required = requiredMap.get(capCode);
Set<Long> candidates = capabilityPersonMap.get(capCode);
int assigned = 0;
if (candidates != null) {
for (Long pid : candidates) {
if (assigned >= required) {
break;
}
if (usedPersonIds.add(pid)) {
assigned++;
}
}
}
assignedMap.put(capCode, assigned);
} }
return assignedMap; return assignedMap;
} }
/**
* Dinic 使
* <p>
* "一人一岗"
*
* <p>
* : source (cap=1) (cap=1) sink(cap=)
* =
*/
private static class MaxFlow {
private final int n;
private final List<int[]> edges;
private final List<List<Integer>> adj;
private final int[] level;
private final int[] iter;
MaxFlow(int n) {
this.n = n;
this.edges = new ArrayList<>();
this.adj = new ArrayList<>();
for (int i = 0; i < n; i++) {
adj.add(new ArrayList<>());
}
this.level = new int[n];
this.iter = new int[n];
}
void addEdge(int from, int to, int cap) {
addEdgeReturnId(from, to, cap);
}
int addEdgeReturnId(int from, int to, int cap) {
int edgeId = edges.size();
adj.get(from).add(edgeId);
edges.add(new int[]{to, cap});
adj.get(to).add(edgeId + 1);
edges.add(new int[]{from, 0});
return edgeId;
}
/**
*
*/
int getFlow(int edgeId) {
return edges.get(edgeId ^ 1)[1];
}
int maxFlow(int s, int t) {
int flow = 0;
while (bfs(s, t)) {
Arrays.fill(iter, 0);
int f;
while ((f = dfs(s, t, Integer.MAX_VALUE)) > 0) {
flow += f;
}
}
return flow;
}
private boolean bfs(int s, int t) {
Arrays.fill(level, -1);
Queue<Integer> queue = new ArrayDeque<>();
level[s] = 0;
queue.add(s);
while (!queue.isEmpty()) {
int v = queue.poll();
for (int ei : adj.get(v)) {
int[] e = edges.get(ei);
if (e[1] > 0 && level[e[0]] < 0) {
level[e[0]] = level[v] + 1;
queue.add(e[0]);
}
}
}
return level[t] >= 0;
}
private int dfs(int v, int t, int f) {
if (v == t) return f;
for (; iter[v] < adj.get(v).size(); iter[v]++) {
int ei = adj.get(v).get(iter[v]);
int[] e = edges.get(ei);
if (e[1] > 0 && level[v] < level[e[0]]) {
int d = dfs(e[0], t, Math.min(f, e[1]));
if (d > 0) {
e[1] -= d;
edges.get(ei ^ 1)[1] += d;
return d;
}
}
}
return 0;
}
}
} }