|
|
|
@ -35,6 +35,7 @@ import java.util.Map; |
|
|
|
import java.util.Set; |
|
|
|
import java.util.UUID; |
|
|
|
import java.util.Collection; |
|
|
|
import java.util.Collections; |
|
|
|
import java.util.stream.Collectors; |
|
|
|
import org.dromara.common.core.exception.ServiceException; |
|
|
|
import org.dromara.common.satoken.utils.LoginHelper; |
|
|
|
@ -758,7 +759,7 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService { |
|
|
|
} |
|
|
|
|
|
|
|
// 2. 获取最大联合单位数量
|
|
|
|
int maxUnionUnit = cpSettingService.getMaxUnionUnit(); |
|
|
|
int maxUnionUnit = cpSettingService.getMaxAutoMatchUnionUnit(); |
|
|
|
|
|
|
|
// 2.1 获取系统匹配预约周数范围
|
|
|
|
int systemMatchWeeks = cpSettingService.getSystemMatchWeeks(); |
|
|
|
@ -769,8 +770,8 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService { |
|
|
|
LambdaQueryWrapper<EfcodeCpPlan> lqw = Wrappers.lambdaQuery(); |
|
|
|
lqw.gt(EfcodeCpPlan::getPlanDate, now); |
|
|
|
lqw.eq(EfcodeCpPlan::getSystemReserved, 0L); |
|
|
|
lqw.eq(EfcodeCpPlan::getManualReserved, 0L); |
|
|
|
lqw.eq(EfcodeCpPlan::getReserveStatus, 1L); |
|
|
|
//lqw.eq(EfcodeCpPlan::getManualReserved, 0L);
|
|
|
|
lqw.in(EfcodeCpPlan::getReserveStatus, 1L, 4L, 5L); |
|
|
|
lqw.eq(EfcodeCpPlan::getIsPublic, 1L); |
|
|
|
lqw.eq(EfcodeCpPlan::getIsUnion, 1L); |
|
|
|
lqw.eq(EfcodeCpPlan::getIsDelete, 0L); |
|
|
|
@ -785,6 +786,33 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService { |
|
|
|
|
|
|
|
log.info("[CpPlan-V2] 共查询到{}条待匹配计划", pendingPlans.size()); |
|
|
|
|
|
|
|
// 预查询所有待匹配计划的手动联合记录,构建互斥关系映射(已手动联合的计划对不应再被放入同一系统组)
|
|
|
|
Set<String> pendingPlanIdSet = pendingPlans.stream() |
|
|
|
.map(EfcodeCpPlan::getId) |
|
|
|
.collect(Collectors.toSet()); |
|
|
|
List<EfcodeCpReserve> allManualReserves = efcodeCpReserveMapper.selectList( |
|
|
|
Wrappers.<EfcodeCpReserve>lambdaQuery() |
|
|
|
.in(EfcodeCpReserve::getPlanId, pendingPlanIdSet) |
|
|
|
.eq(EfcodeCpReserve::getResFrom, 0L) |
|
|
|
); |
|
|
|
// 按planGroupId分组,找出同组内的计划(即已手动联合过的计划对)
|
|
|
|
Map<String, Set<String>> manualGroupToPlanIds = allManualReserves.stream() |
|
|
|
.filter(r -> StringUtils.isNotBlank(r.getPlanGroupId())) |
|
|
|
.collect(Collectors.groupingBy( |
|
|
|
EfcodeCpReserve::getPlanGroupId, |
|
|
|
Collectors.mapping(EfcodeCpReserve::getPlanId, Collectors.toSet()) |
|
|
|
)); |
|
|
|
// 构建每个计划的手动联合伙伴映射: planId -> 已与其手动联合过的其他planId集合
|
|
|
|
Map<String, Set<String>> manualUnionPeers = new HashMap<>(); |
|
|
|
for (Set<String> groupPlanIds : manualGroupToPlanIds.values()) { |
|
|
|
if (groupPlanIds.size() < 2) continue; |
|
|
|
for (String pid : groupPlanIds) { |
|
|
|
manualUnionPeers.computeIfAbsent(pid, k -> new HashSet<>()).addAll(groupPlanIds); |
|
|
|
} |
|
|
|
} |
|
|
|
manualUnionPeers.forEach((pid, peers) -> peers.remove(pid)); |
|
|
|
log.info("[CpPlan-V2] 存在手动联合互斥关系的计划数量: {}", manualUnionPeers.size()); |
|
|
|
|
|
|
|
// 存储将要保存的联合记录列表
|
|
|
|
List<EfcodeCpReserve> unionReserveList = new ArrayList<>(); |
|
|
|
|
|
|
|
@ -809,7 +837,10 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService { |
|
|
|
LocalDate weekEndDate = weekStartDate.plusWeeks(systemMatchWeeks).minusDays(1); // 扩展N周后的周日
|
|
|
|
log.info("[CpPlan-V2] 计划ID={}的时间范围是:{} ~ {}", plan.getId(), weekStartDate, weekEndDate); |
|
|
|
|
|
|
|
// 6. 从待匹配计划中筛选:同企业 + 时间范围内 + 不是当前计划创建者发布的 + 未被处理过
|
|
|
|
// 6. 获取当前计划的手动联合伙伴(与当前计划已手动联合过的计划ID)
|
|
|
|
Set<String> currentPlanPeers = manualUnionPeers.getOrDefault(plan.getId(), Collections.emptySet()); |
|
|
|
|
|
|
|
// 7. 从待匹配计划中筛选:同企业 + 时间范围内 + 不是当前计划创建者发布的 + 未被处理过 + 未与当前计划手动联合过
|
|
|
|
List<EfcodeCpPlan> matchedPlans = pendingPlans.stream() |
|
|
|
.filter(p -> { |
|
|
|
// 排除自身
|
|
|
|
@ -828,19 +859,31 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService { |
|
|
|
if (plan.getCreateBy() != null && plan.getCreateBy().equals(p.getCreateBy())) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
// 排除与当前计划已手动联合过的计划
|
|
|
|
if (currentPlanPeers.contains(p.getId())) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
// 在时间范围内
|
|
|
|
LocalDate pDate = p.getPlanDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); |
|
|
|
return !pDate.isBefore(weekStartDate) && !pDate.isAfter(weekEndDate); |
|
|
|
}) |
|
|
|
.collect(Collectors.toList()); |
|
|
|
|
|
|
|
log.info("[CpPlan-V2] 计划ID={}, 在时间范围内找到{}条非本人发布的同企业计划", plan.getId(), matchedPlans.size()); |
|
|
|
log.info("[CpPlan-V2] 计划ID={}, 在时间范围内找到{}条非本人发布的同企业候选计划", plan.getId(), matchedPlans.size()); |
|
|
|
|
|
|
|
// 7. 从matchedPlans中取出不超过 maxUnionUnit-1 条记录(当前计划本身占1个名额)
|
|
|
|
// 8. 从matchedPlans中选取计划,确保同组内不含已手动联合过的计划对
|
|
|
|
int limit = maxUnionUnit - 1; |
|
|
|
List<EfcodeCpPlan> selectedPlans = matchedPlans.stream() |
|
|
|
.limit(limit) |
|
|
|
.collect(Collectors.toList()); |
|
|
|
List<EfcodeCpPlan> selectedPlans = new ArrayList<>(); |
|
|
|
for (EfcodeCpPlan candidate : matchedPlans) { |
|
|
|
if (selectedPlans.size() >= limit) break; |
|
|
|
// 检查候选计划与已选中计划之间是否存在手动联合互斥关系
|
|
|
|
Set<String> candidatePeers = manualUnionPeers.getOrDefault(candidate.getId(), Collections.emptySet()); |
|
|
|
boolean hasConflict = selectedPlans.stream() |
|
|
|
.anyMatch(sp -> candidatePeers.contains(sp.getId())); |
|
|
|
if (!hasConflict) { |
|
|
|
selectedPlans.add(candidate); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
log.info("[CpPlan-V2] 计划ID={}, 最终选取{}条计划参与联合(最大允许{}条)", plan.getId(), selectedPlans.size(), limit); |
|
|
|
|
|
|
|
@ -917,6 +960,40 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService { |
|
|
|
return resultMap; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 获取指定计划之前通过手动匹配已联合过的其他计划ID集合 |
|
|
|
* |
|
|
|
* @param planId 当前计划ID |
|
|
|
* @return 已手动联合过的计划ID集合,无则返回空集合 |
|
|
|
*/ |
|
|
|
private Set<String> getManualUnionPlanIds(String planId) { |
|
|
|
// 查询当前plan在efcode_cp_reserve中手动匹配(res_from=0)的记录
|
|
|
|
List<EfcodeCpReserve> manualReserves = efcodeCpReserveMapper.selectList( |
|
|
|
Wrappers.<EfcodeCpReserve>lambdaQuery() |
|
|
|
.eq(EfcodeCpReserve::getPlanId, planId) |
|
|
|
.eq(EfcodeCpReserve::getResFrom, 0L) |
|
|
|
); |
|
|
|
if (manualReserves.isEmpty()) { |
|
|
|
return Collections.emptySet(); |
|
|
|
} |
|
|
|
Set<String> manualGroupIds = manualReserves.stream() |
|
|
|
.map(EfcodeCpReserve::getPlanGroupId) |
|
|
|
.filter(StringUtils::isNotBlank) |
|
|
|
.collect(Collectors.toSet()); |
|
|
|
if (manualGroupIds.isEmpty()) { |
|
|
|
return Collections.emptySet(); |
|
|
|
} |
|
|
|
// 查询同组中其他计划的planId
|
|
|
|
List<EfcodeCpReserve> groupedReserves = efcodeCpReserveMapper.selectList( |
|
|
|
Wrappers.<EfcodeCpReserve>lambdaQuery() |
|
|
|
.in(EfcodeCpReserve::getPlanGroupId, manualGroupIds) |
|
|
|
.ne(EfcodeCpReserve::getPlanId, planId) |
|
|
|
); |
|
|
|
return groupedReserves.stream() |
|
|
|
.map(EfcodeCpReserve::getPlanId) |
|
|
|
.collect(Collectors.toSet()); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 计划自动匹配预约V2(定时任务调用) |
|
|
|
* <p>扫描待预约状态的计划,按自然周分组匹配,超出最大联合数量时拆分多组</p> |
|
|
|
|