|
|
|
@ -293,7 +293,15 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService { |
|
|
|
lqw.eq(bo.getIsPublic() != null, EfcodeCpPlan::getIsPublic, bo.getIsPublic()); |
|
|
|
lqw.eq(bo.getIsUnion() != null, EfcodeCpPlan::getIsUnion, bo.getIsUnion()); |
|
|
|
lqw.eq(StringUtils.isNotBlank(bo.getUnionBureauDeptNames()), EfcodeCpPlan::getUnionBureauDeptNames, bo.getUnionBureauDeptNames()); |
|
|
|
lqw.eq(bo.getReserveStatus() != null, EfcodeCpPlan::getReserveStatus, bo.getReserveStatus()); |
|
|
|
// 支持多值状态过滤(逗号分隔)
|
|
|
|
String reserveStatusList = params != null ? (String) params.get("reserveStatusList") : null; |
|
|
|
if (StringUtils.isNotBlank(reserveStatusList)) { |
|
|
|
List<Long> statusList = java.util.Arrays.stream(reserveStatusList.split(",")) |
|
|
|
.map(String::trim).map(Long::valueOf).collect(Collectors.toList()); |
|
|
|
lqw.in(EfcodeCpPlan::getReserveStatus, statusList); |
|
|
|
} else { |
|
|
|
lqw.eq(bo.getReserveStatus() != null, EfcodeCpPlan::getReserveStatus, bo.getReserveStatus()); |
|
|
|
} |
|
|
|
lqw.eq(bo.getSystemReserved() != null, EfcodeCpPlan::getSystemReserved, bo.getSystemReserved()); |
|
|
|
lqw.eq(bo.getManualReserved() != null, EfcodeCpPlan::getManualReserved, bo.getManualReserved()); |
|
|
|
lqw.eq(StringUtils.isNotBlank(bo.getBureauDeptId()), EfcodeCpPlan::getBureauDeptId, bo.getBureauDeptId()); |
|
|
|
@ -732,6 +740,182 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService { |
|
|
|
return resultMap; |
|
|
|
} |
|
|
|
|
|
|
|
@Transactional(rollbackFor = Exception.class) |
|
|
|
@Override |
|
|
|
public Map<String, List<EfcodeCpReserve>> autoMatchReserveV3(boolean forceMatch, boolean updatePlan, boolean insertReserve) { |
|
|
|
log.info("[CpPlan-V2] 开始执行计划自动匹配预约, forceMatch={}, updatePlan={}, insertReserve={}", forceMatch, updatePlan, insertReserve); |
|
|
|
Map<String, List<EfcodeCpReserve>> resultMap = new HashMap<>(); |
|
|
|
|
|
|
|
// 1. 日期校验:如果不是强制匹配,则判断当前天数是否等于 CP_SETTING_AUTO_MATCH_START
|
|
|
|
if (!forceMatch) { |
|
|
|
int autoMatchStart = cpSettingService.getAutoMatchStart(); |
|
|
|
int today = LocalDate.now().getDayOfMonth(); |
|
|
|
if (today != autoMatchStart) { |
|
|
|
log.info("[CpPlan-V2] 当前日期{}号,不等于设定的自动匹配日期{}号,跳过执行", today, autoMatchStart); |
|
|
|
return resultMap; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 2. 获取最大联合单位数量
|
|
|
|
int maxUnionUnit = cpSettingService.getMaxUnionUnit(); |
|
|
|
|
|
|
|
// 2.1 获取系统匹配预约周数范围
|
|
|
|
int systemMatchWeeks = cpSettingService.getSystemMatchWeeks(); |
|
|
|
log.info("[CpPlan-V2] 系统匹配周数范围={}", systemMatchWeeks); |
|
|
|
|
|
|
|
// 3. 查询待匹配的计划:plan_date > 当前时间 且 system_reserved=0, manual_reserved=0, reserve_status=1, is_public=1, is_union=1
|
|
|
|
Date now = new Date(); |
|
|
|
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::getIsPublic, 1L); |
|
|
|
lqw.eq(EfcodeCpPlan::getIsUnion, 1L); |
|
|
|
lqw.eq(EfcodeCpPlan::getIsDelete, 0L); |
|
|
|
lqw.isNotNull(EfcodeCpPlan::getCreateBy); |
|
|
|
lqw.orderByAsc(EfcodeCpPlan::getPlanDate); |
|
|
|
List<EfcodeCpPlan> pendingPlans = baseMapper.selectList(lqw); |
|
|
|
|
|
|
|
if (pendingPlans.isEmpty()) { |
|
|
|
log.info("[CpPlan-V2] 没有找到待匹配的计划记录,执行结束"); |
|
|
|
return resultMap; |
|
|
|
} |
|
|
|
|
|
|
|
log.info("[CpPlan-V2] 共查询到{}条待匹配计划", pendingPlans.size()); |
|
|
|
|
|
|
|
// 存储将要保存的联合记录列表
|
|
|
|
List<EfcodeCpReserve> unionReserveList = new ArrayList<>(); |
|
|
|
|
|
|
|
// 记录已被处理(system_reserved标记为1)的计划ID,避免重复处理
|
|
|
|
Set<String> processedPlanIds = new HashSet<>(); |
|
|
|
int matchedCount = 0; |
|
|
|
|
|
|
|
for (EfcodeCpPlan plan : pendingPlans) { |
|
|
|
// 已被处理过的计划跳过
|
|
|
|
if (processedPlanIds.contains(plan.getId())) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
log.info("[CpPlan-V2] 开始处理计划记录,计划ID={}", plan.getId()); |
|
|
|
String enterpriseNumber = plan.getEnterpriseNumber(); |
|
|
|
if (StringUtils.isBlank(enterpriseNumber)) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
// 5. 计算计划所在的时间范围:以计划所在自然周的周一为起始,向后扩展 systemMatchWeeks 周
|
|
|
|
LocalDate planLocalDate = plan.getPlanDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); |
|
|
|
LocalDate weekStartDate = planLocalDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); |
|
|
|
LocalDate weekEndDate = weekStartDate.plusWeeks(systemMatchWeeks).minusDays(1); // 扩展N周后的周日
|
|
|
|
log.info("[CpPlan-V2] 计划ID={}的时间范围是:{} ~ {}", plan.getId(), weekStartDate, weekEndDate); |
|
|
|
|
|
|
|
// 6. 从待匹配计划中筛选:同企业 + 时间范围内 + 不是当前计划创建者发布的 + 未被处理过
|
|
|
|
List<EfcodeCpPlan> matchedPlans = pendingPlans.stream() |
|
|
|
.filter(p -> { |
|
|
|
// 排除自身
|
|
|
|
if (p.getId().equals(plan.getId())) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
// 排除已处理过的
|
|
|
|
if (processedPlanIds.contains(p.getId())) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
// 同企业
|
|
|
|
if (!enterpriseNumber.equals(p.getEnterpriseNumber())) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
// 不是当前计划创建者发布的(不是我的计划)
|
|
|
|
if (plan.getCreateBy() != null && plan.getCreateBy().equals(p.getCreateBy())) { |
|
|
|
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()); |
|
|
|
|
|
|
|
// 7. 从matchedPlans中取出不超过 maxUnionUnit-1 条记录(当前计划本身占1个名额)
|
|
|
|
int limit = maxUnionUnit - 1; |
|
|
|
List<EfcodeCpPlan> selectedPlans = matchedPlans.stream() |
|
|
|
.limit(limit) |
|
|
|
.collect(Collectors.toList()); |
|
|
|
|
|
|
|
log.info("[CpPlan-V2] 计划ID={}, 最终选取{}条计划参与联合(最大允许{}条)", plan.getId(), selectedPlans.size(), limit); |
|
|
|
|
|
|
|
// 8. 如果没有可联合的计划,跳过
|
|
|
|
if (selectedPlans.isEmpty()) { |
|
|
|
log.debug("[CpPlan-V2] 计划ID={} 没有找到可联合的计划,跳过", plan.getId()); |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
// 9. 将当前plan和selectedPlans作为一组,生成预约记录
|
|
|
|
String planGroupId = "Temp" + "_" + UUID.randomUUID().toString().replace("-", ""); |
|
|
|
Date reserveTime = new Date(); |
|
|
|
List<EfcodeCpReserve> groupReserves = new ArrayList<>(); |
|
|
|
|
|
|
|
// 组内所有计划(当前计划 + 选中的计划)
|
|
|
|
List<EfcodeCpPlan> groupPlans = new ArrayList<>(); |
|
|
|
groupPlans.add(plan); |
|
|
|
groupPlans.addAll(selectedPlans); |
|
|
|
|
|
|
|
// 拼接参与单位名称
|
|
|
|
String unitNames = groupPlans.stream() |
|
|
|
.map(EfcodeCpPlan::getBureauDeptName) |
|
|
|
.filter(StringUtils::isNotBlank) |
|
|
|
.collect(Collectors.joining(",")); |
|
|
|
String opMessage = "由于计划执法时间安排较近,系统推荐进行联合,参与单位为:" + unitNames + ".请确认."; |
|
|
|
|
|
|
|
for (EfcodeCpPlan matchedPlan : groupPlans) { |
|
|
|
EfcodeCpReserve reserve = new EfcodeCpReserve(); |
|
|
|
reserve.setPlanId(matchedPlan.getId()); |
|
|
|
reserve.setPlanGroupId(planGroupId); |
|
|
|
reserve.setReserveTime(reserveTime); |
|
|
|
reserve.setOpStatus(3L); // 待确认
|
|
|
|
reserve.setOpOrder(0L); // 系统匹配默认为0
|
|
|
|
reserve.setOpMessage(opMessage); |
|
|
|
reserve.setOpSetDate(matchedPlan.getPlanDate()); |
|
|
|
reserve.setOpSetTime(matchedPlan.getPlanTime()); |
|
|
|
reserve.setResFrom(1L); // 系统匹配
|
|
|
|
reserve.setSenderId("from_system"); |
|
|
|
reserve.setReceiverId(String.valueOf(matchedPlan.getCreateBy())); |
|
|
|
|
|
|
|
if (insertReserve) { |
|
|
|
efcodeCpReserveMapper.insert(reserve); |
|
|
|
} |
|
|
|
groupReserves.add(reserve); |
|
|
|
} |
|
|
|
|
|
|
|
// 加入总的待处理列表
|
|
|
|
unionReserveList.addAll(groupReserves); |
|
|
|
resultMap.put(planGroupId, groupReserves); |
|
|
|
|
|
|
|
// 10. 将组内所有计划标记为 system_reserved=1,并更新 reserve_status=2(预约中)
|
|
|
|
if (updatePlan) { |
|
|
|
for (EfcodeCpPlan gp : groupPlans) { |
|
|
|
EfcodeCpPlan planUpdate = new EfcodeCpPlan(); |
|
|
|
planUpdate.setId(gp.getId()); |
|
|
|
planUpdate.setSystemReserved(1L); |
|
|
|
planUpdate.setReserveStatus(2L); |
|
|
|
baseMapper.updateById(planUpdate); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 将组内所有计划ID加入已处理集合,防止重复匹配
|
|
|
|
for (EfcodeCpPlan gp : groupPlans) { |
|
|
|
processedPlanIds.add(gp.getId()); |
|
|
|
} |
|
|
|
|
|
|
|
matchedCount++; |
|
|
|
log.info("[CpPlan-V2] 计划ID={} 匹配成功,联合{}个单位,planGroupId={}", |
|
|
|
plan.getId(), groupPlans.size(), planGroupId); |
|
|
|
} |
|
|
|
|
|
|
|
log.info("[CpPlan-V2] 计划自动匹配预约执行完毕,共匹配{}组联合预约,共生成{}条预约记录", matchedCount, unionReserveList.size()); |
|
|
|
|
|
|
|
return resultMap; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 计划自动匹配预约V2(定时任务调用) |
|
|
|
* <p>扫描待预约状态的计划,按自然周分组匹配,超出最大联合数量时拆分多组</p> |
|
|
|
@ -822,7 +1006,7 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService { |
|
|
|
weekLqw.orderByAsc(EfcodeCpPlan::getPlanDate); |
|
|
|
List<EfcodeCpPlan> samePlans = baseMapper.selectList(weekLqw); |
|
|
|
|
|
|
|
// 7. 按不同执法单位去重,取每个 bureau_dept_id 的第一条计划作为代表
|
|
|
|
// 7. 按 bureau_dept_id 去重,每个单位取一条代表计划
|
|
|
|
List<EfcodeCpPlan> distinctUnitPlans = new ArrayList<>(); |
|
|
|
Set<String> seenBureauDeptIds = new HashSet<>(); |
|
|
|
for (EfcodeCpPlan sp : samePlans) { |
|
|
|
@ -833,18 +1017,17 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 8. 如果只有一个单位的计划,说明没有可联合的,跳过
|
|
|
|
// 8. 只有1个单位的计划,无法联合,跳过
|
|
|
|
if (distinctUnitPlans.size() <= 1) { |
|
|
|
log.debug("[CpPlan-V2] 企业[{}] 周[{}~{}]内只有1个单位的计划,跳过", enterpriseNumber, weekStartDate, weekEndDate); |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
// 9. 按最大联合单位数量分组:如果不同单位数 > maxUnionUnit,则拆分成多组
|
|
|
|
// 9. 按最大联合单位数量分组
|
|
|
|
List<List<EfcodeCpPlan>> groups = new ArrayList<>(); |
|
|
|
for (int i = 0; i < distinctUnitPlans.size(); i += maxUnionUnit) { |
|
|
|
int end = Math.min(i + maxUnionUnit, distinctUnitPlans.size()); |
|
|
|
List<EfcodeCpPlan> group = distinctUnitPlans.subList(i, end); |
|
|
|
// 每组至少需要2个不同单位才有联合意义
|
|
|
|
if (group.size() >= 2) { |
|
|
|
groups.add(new ArrayList<>(group)); |
|
|
|
} |
|
|
|
@ -855,12 +1038,12 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService { |
|
|
|
} |
|
|
|
|
|
|
|
// 10. 为每组生成预约记录
|
|
|
|
Set<String> matchedPlanIds = new HashSet<>(); |
|
|
|
for (List<EfcodeCpPlan> group : groups) { |
|
|
|
String planGroupId = "Temp" + "_" + UUID.randomUUID().toString().replace("-", ""); |
|
|
|
Date reserveTime = new Date(); |
|
|
|
List<EfcodeCpReserve> groupReserves = new ArrayList<>(); |
|
|
|
|
|
|
|
// 拼接参与单位名称,用于 opMessage
|
|
|
|
String unitNames = group.stream() |
|
|
|
.map(EfcodeCpPlan::getBureauDeptName) |
|
|
|
.filter(StringUtils::isNotBlank) |
|
|
|
@ -887,7 +1070,7 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService { |
|
|
|
groupReserves.add(reserve); |
|
|
|
} |
|
|
|
|
|
|
|
// 更新被选中参与联合的计划: reserve_status=2(预约中)
|
|
|
|
// 更新被选中参与联合的计划: reserve_status=2(预约中), system_reserved=1
|
|
|
|
if (updatePlan) { |
|
|
|
for (EfcodeCpPlan dp : group) { |
|
|
|
EfcodeCpPlan planUpdate = new EfcodeCpPlan(); |
|
|
|
@ -895,27 +1078,28 @@ public class EfcodeCpPlanServiceImpl implements IEfcodeCpPlanService { |
|
|
|
planUpdate.setReserveStatus(2L); |
|
|
|
planUpdate.setSystemReserved(1L); |
|
|
|
baseMapper.updateById(planUpdate); |
|
|
|
matchedPlanIds.add(dp.getId()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
resultMap.put(planGroupId, groupReserves); |
|
|
|
matchedCount++; |
|
|
|
log.info("[CpPlan-V2] 企业[{}] 周[{}~{}] 分组匹配成功,联合{}个单位,planGroupId={}", |
|
|
|
log.info("[CpPlan-V2] 企业[{}] 周[{}~{}] 匹配成功,联合{}个单位,planGroupId={}", |
|
|
|
enterpriseNumber, weekStartDate, weekEndDate, group.size(), planGroupId); |
|
|
|
} |
|
|
|
|
|
|
|
// 11. 将该企业该周所有待匹配计划的 system_reserved 标记为1,防止重复匹配
|
|
|
|
if (updatePlan) { |
|
|
|
// 11. 将已成功匹配的单位在该企业该周内的所有计划标记为 system_reserved=1,防止重复匹配
|
|
|
|
if (updatePlan && !groups.isEmpty()) { |
|
|
|
for (EfcodeCpPlan sp : samePlans) { |
|
|
|
// 已在上面group循环中更新过的(reserve_status=2)跳过,避免覆盖
|
|
|
|
if (seenBureauDeptIds.contains(sp.getBureauDeptId())) { |
|
|
|
// 该计划对应的单位已经参与联合,在group循环中已处理
|
|
|
|
if (matchedPlanIds.contains(sp.getId())) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
EfcodeCpPlan planUpdate = new EfcodeCpPlan(); |
|
|
|
planUpdate.setId(sp.getId()); |
|
|
|
planUpdate.setSystemReserved(1L); |
|
|
|
baseMapper.updateById(planUpdate); |
|
|
|
if (seenBureauDeptIds.contains(sp.getBureauDeptId())) { |
|
|
|
EfcodeCpPlan planUpdate = new EfcodeCpPlan(); |
|
|
|
planUpdate.setId(sp.getId()); |
|
|
|
planUpdate.setSystemReserved(1L); |
|
|
|
baseMapper.updateById(planUpdate); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|