dev_1.0.1
luotaiqian 2026-07-16 14:40:45 +08:00
parent b313663589
commit 76070ba58a
1 changed files with 209 additions and 29 deletions

View File

@ -342,11 +342,17 @@ public class ComplianceCheckUtil {
}
/**
*
*
* <p>
* sourcePersonnelId
* 使Dinic
* <p>
* certCapabilityList personnelId == industryProfessionalPersonCos sourcePersonnelId
*
*
* @param standardsDOList code + code
* @param industryProfessionalPersonCos code + code
* @param certCapabilityList certCapabilityList
* @param industryProfessionalPersonCos
* @param certCapabilityList
* @return Pair<Boolean, String> key value null
*/
public static Pair<Boolean, String> doIndustryProfessionalStandardsValid(List<IndustryProfessionalStandardsDO> standardsDOList
@ -355,38 +361,212 @@ public class ComplianceCheckUtil {
return Pair.of(true, null);
}
// 当前人员数按 行业code + 专业能力code 分类汇总
Map<String, Long> currentCountMap = industryProfessionalPersonCos.stream()
.collect(Collectors.groupingBy(
c -> groupKey(c.getIndustryCode(), c.getProfessionalCapabilityCode()),
Collectors.summingLong(c -> c.getPersonNum() == null ? 0L : c.getPersonNum())));
// 1. 汇总每个专业能力code的要求人数
Map<String, Integer> requiredMap = standardsDOList.stream()
.collect(Collectors.toMap(
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集合同一人可能具备多个专业能力
Map<Long, Set<String>> personCapabilityMap = new HashMap<>();
for (OrgPersonnelCertCapabilityComplianceCheckCO cert : certCapabilityList) {
Long pid = cert.getPersonnelId();
if (pid != null && filingPersonIds.contains(pid) && cert.getProfessionalCapabilityCode() != null) {
personCapabilityMap.computeIfAbsent(pid, k -> new HashSet<>())
.add(cert.getProfessionalCapabilityCode());
}
}
// 4. 一人一岗最优分配
Map<String, Integer> assignedMap = assignPersonnelByMaxFlow(personCapabilityMap, requiredMap);
// 5. 判断是否所有专业能力都达标,并生成失败消息
boolean allMet = true;
IndustryEnum industryEnum = IndustryEnum.ofCode(standardsDOList.get(0).getIndustryCode());
String industryName = industryEnum != null ? industryEnum.getValue() : standardsDOList.get(0).getIndustryCode();
// 要求按 行业code + 专业能力code 分类汇总,并记录专业能力名称
StringBuilder failureMsg = new StringBuilder();
for (IndustryProfessionalStandardsDO s : standardsDOList) {
String key = groupKey(s.getIndustryCode(), s.getProfessionalCapabilityCode());
Long currentPerson = currentCountMap.getOrDefault(key, 0L);
if (s.getPersonNum() > currentPerson) {
IndustryEnum industryEnum = IndustryEnum.ofCode(s.getIndustryCode());
ProfessionalCapabilityEnum professionalCapabilityEnum = ProfessionalCapabilityEnum.ofCode(s.getProfessionalCapabilityCode());
failureMsg.append(industryEnum.getValue())
for (Map.Entry<String, Integer> entry : requiredMap.entrySet()) {
String capCode = entry.getKey();
int required = entry.getValue();
int assigned = assignedMap.getOrDefault(capCode, 0);
if (assigned < required) {
allMet = false;
ProfessionalCapabilityEnum capEnum = ProfessionalCapabilityEnum.ofCode(capCode);
String capName = capEnum != null ? capEnum.getValue() : capCode;
failureMsg.append(industryName)
.append(":")
.append(professionalCapabilityEnum.getValue())
.append(capName)
.append(" 需要")
.append(s.getPersonNum())
.append("人,当前")
.append(currentPerson)
.append("人");
.append(required)
.append("人,因一人一岗最多可分配")
.append(assigned)
.append("人");
}
}
return failureMsg.length() > 0 ? Pair.of(false, failureMsg.toString()) : Pair.of(true, null);
return allMet ? Pair.of(true, null) : Pair.of(false, failureMsg.toString());
}
/**
* code + code
* / Dinic
* <p>
*
* "一人一岗"
* <p>
* : source (cap=1) (cap=1) sink(cap=)
*
* @param personCapabilityMap code
* @param requiredMap code
* @return code
*/
private static String groupKey(String industryCode, String professionalCapabilityCode) {
return (industryCode == null ? "" : industryCode) + "_"
+ (professionalCapabilityCode == null ? "" : professionalCapabilityCode);
private static Map<String, Integer> assignPersonnelByMaxFlow(Map<Long, Set<String>> personCapabilityMap
, Map<String, Integer> requiredMap) {
List<Long> personIds = new ArrayList<>(personCapabilityMap.keySet());
List<String> capCodes = new ArrayList<>(requiredMap.keySet());
Map<String, Integer> capIndex = new HashMap<>();
for (int j = 0; j < capCodes.size(); j++) {
capIndex.put(capCodes.get(j), j);
}
int numPersons = personIds.size();
int numCaps = capCodes.size();
int source = 0;
int sink = numPersons + numCaps + 1;
MaxFlow mf = new MaxFlow(sink + 1);
// source → person (capacity 1: 每人最多分配一个岗位)
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<>();
for (int j = 0; j < numCaps; j++) {
assignedMap.put(capCodes.get(j), mf.getFlow(capEdgeIds[j]));
}
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;
}
}
}