插活
This commit is contained in:
		
							parent
							
								
									88529941bf
								
							
						
					
					
						commit
						05383c2355
					
				@ -315,7 +315,13 @@ public class PlanSubDetailServiceImpl implements PlanSubDetailService {
 | 
			
		||||
                // 判断请求时间段是否与已有时间段有重叠
 | 
			
		||||
                boolean hasOverlap = !(reqEnd.compareTo(existStart) < 0) && !(reqStart.compareTo(existEnd) > 0);
 | 
			
		||||
                if (hasOverlap) {
 | 
			
		||||
                    return CommonResult.error(400, "该子项目设计时间存在交叉,请确认!");
 | 
			
		||||
                    String type="BLUEPRINT_WORKBLANK".equals(updateReqVO.getSubType())?"毛坯":"BLUEPRINT_2D".equals(updateReqVO.getSubType())?"2D":"3D";
 | 
			
		||||
 | 
			
		||||
                    String type2="BLUEPRINT_WORKBLANK".equals(planSubDetailDO.getSubType())?"毛坯":"BLUEPRINT_2D".equals(planSubDetailDO.getSubType())?"2D":"3D";
 | 
			
		||||
 | 
			
		||||
                    return CommonResult.error(400, "子项目:"+updateReqVO.getName()+" 设计类型:"+type +" "+reqStart.toString().substring(0,10)+" "+reqEnd.toString().substring(0,10)
 | 
			
		||||
                            +"与"+"子项目:"+planSubDetailDO.getName()+" 设计类型:"+type2 +" "+existStart.toString().substring(0,10)+" "+existEnd.toString().substring(0,10)
 | 
			
		||||
                            +"设计时间存在交叉,请确认!");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@ -424,20 +430,27 @@ public class PlanSubDetailServiceImpl implements PlanSubDetailService {
 | 
			
		||||
        planSubDetailMapper.updateBatch(list);
 | 
			
		||||
        //更新表主表数据
 | 
			
		||||
        //先看有多少个子项目
 | 
			
		||||
        Map<Long, List<PlanSubDetailDO>> collect = list.stream().collect(Collectors.groupingBy(PlanSubDetailDO::getProjectPlanSubId));
 | 
			
		||||
        collect.forEach((subId,subList) -> {
 | 
			
		||||
        Map<Long, List<PlanSubDetailDO>> collect = list.stream().collect(Collectors.groupingBy(PlanSubDetailDO::getProjectSubId));
 | 
			
		||||
        for (Map.Entry<Long, List<PlanSubDetailDO>> entry : collect.entrySet()) {
 | 
			
		||||
            Long subId = entry.getKey();
 | 
			
		||||
            List<PlanSubDetailDO> subList = entry.getValue();
 | 
			
		||||
            //再根据设计类型来
 | 
			
		||||
            Map<String, List<PlanSubDetailDO>> collect1 = subList.stream().collect(Collectors.groupingBy(PlanSubDetailDO::getSubType));
 | 
			
		||||
            //然后对里面的进行排序
 | 
			
		||||
            collect1.forEach((type,subTypeList) ->{
 | 
			
		||||
            for (Map.Entry<String, List<PlanSubDetailDO>> entry1 : collect1.entrySet()) {
 | 
			
		||||
                String type = entry1.getKey();
 | 
			
		||||
                List<PlanSubDetailDO> subTypeList = entry1.getValue();
 | 
			
		||||
                List<PlanSubDetailDO> sortedList = subTypeList.stream()
 | 
			
		||||
                        .sorted(Comparator.comparing(PlanSubDetailDO::getSeqNo).reversed())
 | 
			
		||||
                        .collect(Collectors.toList());
 | 
			
		||||
                Long num = 0L;
 | 
			
		||||
                for (PlanSubDetailDO planSubDetailDO : sortedList) {
 | 
			
		||||
                    Long designNum = planSubDetailDO.getDesignNum();
 | 
			
		||||
                    num += designNum;
 | 
			
		||||
                }
 | 
			
		||||
                LambdaUpdateWrapper<PlanSubDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
 | 
			
		||||
                lambdaUpdateWrapper.eq(PlanSubDO::getProjectSubId,subId);
 | 
			
		||||
                LocalDateTime startTwoDimDate = sortedList.get(0).getStartTwoDimDate();
 | 
			
		||||
                LocalDateTime startTwoDimDate = sortedList.get(subTypeList.size()-1).getStartTwoDimDate();
 | 
			
		||||
                LocalDateTime twoDimDate = sortedList.get(0).getTwoDimDate();
 | 
			
		||||
                Long num = sortedList.get(0).getDesignNum();
 | 
			
		||||
                //取最大值就是第一个
 | 
			
		||||
                if ("BLUEPRINT_WORKBLANK".equals(type)){
 | 
			
		||||
                    lambdaUpdateWrapper.set(PlanSubDO::getStartBlankDate,startTwoDimDate);
 | 
			
		||||
@ -453,8 +466,8 @@ public class PlanSubDetailServiceImpl implements PlanSubDetailService {
 | 
			
		||||
                    lambdaUpdateWrapper.set(PlanSubDO::getThreeDimNum,num);
 | 
			
		||||
                }
 | 
			
		||||
                planSubMapper.update(lambdaUpdateWrapper);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return CommonResult.success(true);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
@ -523,7 +536,21 @@ public class PlanSubDetailServiceImpl implements PlanSubDetailService {
 | 
			
		||||
        });
 | 
			
		||||
        return CommonResult.success(true);
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * 计算工作天数)
 | 
			
		||||
     */
 | 
			
		||||
    private Long calculateWorkDays(LocalDateTime startDate, LocalDateTime endDate) {
 | 
			
		||||
        long workDays = 0;
 | 
			
		||||
        LocalDateTime current = startDate.toLocalDate().atStartOfDay();
 | 
			
		||||
        LocalDateTime end = endDate.toLocalDate().atStartOfDay();
 | 
			
		||||
 | 
			
		||||
        while (!current.isAfter(end)) {
 | 
			
		||||
            workDays++;
 | 
			
		||||
            current = current.plusDays(1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return workDays;
 | 
			
		||||
    }
 | 
			
		||||
//    @Override
 | 
			
		||||
//    @Transactional
 | 
			
		||||
//    public CommonResult chahuoList(PlanSubDetailSaveReqVO updateReqVO) {
 | 
			
		||||
@ -987,284 +1014,198 @@ public class PlanSubDetailServiceImpl implements PlanSubDetailService {
 | 
			
		||||
                                                     List<LocalDateTime> holidayList) {
 | 
			
		||||
        List<PlanSubDetailDO> resultList = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
        // 1. 如果没有插活数据,直接返回baseList
 | 
			
		||||
        if (CollUtil.isEmpty(insertList)) {
 | 
			
		||||
        // 1. 先处理插活数据,不按节假日分段,保持原始范围
 | 
			
		||||
        List<PlanSubDetailDO> processedInsertList = new ArrayList<>();
 | 
			
		||||
        for (PlanSubDetailDO insertItem : insertList) {
 | 
			
		||||
            // 插活数据保持原始时间范围,但designNum排除节假日
 | 
			
		||||
            PlanSubDetailDO processedItem = new PlanSubDetailDO();
 | 
			
		||||
            // 复制属性
 | 
			
		||||
            processedItem.setId(null);
 | 
			
		||||
            processedItem.setProjectPlanId(insertItem.getProjectPlanId());
 | 
			
		||||
            processedItem.setProjectId(insertItem.getProjectId());
 | 
			
		||||
            processedItem.setProjectPlanSubId(insertItem.getProjectPlanSubId());
 | 
			
		||||
            processedItem.setProjectSubCode(insertItem.getProjectSubCode());
 | 
			
		||||
            processedItem.setTwoDimOwner(insertItem.getTwoDimOwner());
 | 
			
		||||
            processedItem.setProjectSubId(insertItem.getProjectPlanSubId());
 | 
			
		||||
            processedItem.setSubType(insertItem.getSubType());
 | 
			
		||||
            processedItem.setName(insertItem.getName());
 | 
			
		||||
            processedItem.setCode(insertItem.getCode());
 | 
			
		||||
            processedItem.setStartTwoDimDate(insertItem.getStartTwoDimDate());
 | 
			
		||||
            processedItem.setTwoDimDate(insertItem.getTwoDimDate());
 | 
			
		||||
            processedItem.setIsCha("Y");
 | 
			
		||||
            // designNum排除节假日
 | 
			
		||||
            processedItem.setDesignNum(calculateWorkDaysExcludeHolidays(insertItem.getStartTwoDimDate(), insertItem.getTwoDimDate(), holidayList));
 | 
			
		||||
            processedInsertList.add(processedItem);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 2. 如果没有插活数据,直接返回baseList
 | 
			
		||||
        if (processedInsertList.isEmpty()) {
 | 
			
		||||
            return new ArrayList<>(baseList);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 2. 如果baseList为空,直接返回所有插活数据
 | 
			
		||||
        // 3. 处理baseList为空的情况
 | 
			
		||||
        if (CollUtil.isEmpty(baseList)) {
 | 
			
		||||
            List<PlanSubDetailDO> processedInsertList = new ArrayList<>();
 | 
			
		||||
            for (PlanSubDetailDO insertItem : insertList) {
 | 
			
		||||
                PlanSubDetailDO processedInsert = createInsertItem(insertItem, insertItem.getStartTwoDimDate(), insertItem.getTwoDimDate(), holidayList);
 | 
			
		||||
                processedInsertList.add(processedInsert);
 | 
			
		||||
            }
 | 
			
		||||
            processedInsertList.sort(Comparator.comparing(PlanSubDetailDO::getStartTwoDimDate));
 | 
			
		||||
            return processedInsertList;
 | 
			
		||||
            // baseList为空,直接返回插活数据
 | 
			
		||||
            return new ArrayList<>(processedInsertList);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 3. 获取baseList的时间范围
 | 
			
		||||
        LocalDateTime baseListMinStartTime = baseList.stream()
 | 
			
		||||
        // 4. 获取插活数据的时间范围
 | 
			
		||||
        LocalDateTime insertStartTime = processedInsertList.stream()
 | 
			
		||||
                .map(PlanSubDetailDO::getStartTwoDimDate)
 | 
			
		||||
                .min(LocalDateTime::compareTo)
 | 
			
		||||
                .orElse(null);
 | 
			
		||||
        LocalDateTime baseListMaxEndTime = baseList.stream()
 | 
			
		||||
        LocalDateTime insertEndTime = processedInsertList.stream()
 | 
			
		||||
                .map(PlanSubDetailDO::getTwoDimDate)
 | 
			
		||||
                .max(LocalDateTime::compareTo)
 | 
			
		||||
                .orElse(null);
 | 
			
		||||
 | 
			
		||||
        // 4. 处理插活数据,保持原有时间范围,但重新计算designNum(排除节假日)
 | 
			
		||||
        List<PlanSubDetailDO> processedInsertList = new ArrayList<>();
 | 
			
		||||
        for (PlanSubDetailDO insertItem : insertList) {
 | 
			
		||||
            LocalDateTime startDate = insertItem.getStartTwoDimDate();
 | 
			
		||||
            LocalDateTime endDate = insertItem.getTwoDimDate();
 | 
			
		||||
        // 5. 获取baseList的时间范围
 | 
			
		||||
        LocalDateTime baseMinStartTime = baseList.stream()
 | 
			
		||||
                .map(PlanSubDetailDO::getStartTwoDimDate)
 | 
			
		||||
                .min(LocalDateTime::compareTo)
 | 
			
		||||
                .orElse(null);
 | 
			
		||||
        LocalDateTime baseMaxEndTime = baseList.stream()
 | 
			
		||||
                .map(PlanSubDetailDO::getTwoDimDate)
 | 
			
		||||
                .max(LocalDateTime::compareTo)
 | 
			
		||||
                .orElse(null);
 | 
			
		||||
 | 
			
		||||
            PlanSubDetailDO processedInsert = createInsertItem(insertItem, startDate, endDate, holidayList);
 | 
			
		||||
            processedInsertList.add(processedInsert);
 | 
			
		||||
        // 6. 边界情况处理
 | 
			
		||||
        if (insertEndTime != null && baseMinStartTime != null && insertEndTime.isBefore(baseMinStartTime)) {
 | 
			
		||||
            // 情况1:插活数据在所有baseList之前
 | 
			
		||||
            resultList.addAll(processedInsertList);
 | 
			
		||||
            resultList.addAll(baseList);
 | 
			
		||||
            return resultList;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 5. 分离插活数据:在baseList之前的、在baseList之间的、在baseList之后的
 | 
			
		||||
        List<PlanSubDetailDO> beforeBaseListInserts = new ArrayList<>();
 | 
			
		||||
        List<PlanSubDetailDO> duringBaseListInserts = new ArrayList<>();
 | 
			
		||||
        List<PlanSubDetailDO> afterBaseListInserts = new ArrayList<>();
 | 
			
		||||
        if (insertStartTime != null && baseMaxEndTime != null && insertStartTime.isAfter(baseMaxEndTime)) {
 | 
			
		||||
            // 情况2:插活数据在所有baseList之后
 | 
			
		||||
            resultList.addAll(baseList);
 | 
			
		||||
            resultList.addAll(processedInsertList);
 | 
			
		||||
            return resultList;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (PlanSubDetailDO insertItem : processedInsertList) {
 | 
			
		||||
            if (insertItem.getTwoDimDate().isBefore(baseListMinStartTime)) {
 | 
			
		||||
                // 插活数据完全在baseList之前
 | 
			
		||||
                beforeBaseListInserts.add(insertItem);
 | 
			
		||||
            } else if (insertItem.getStartTwoDimDate().isAfter(baseListMaxEndTime)) {
 | 
			
		||||
                // 插活数据完全在baseList之后
 | 
			
		||||
                afterBaseListInserts.add(insertItem);
 | 
			
		||||
        // 7. 正常插活逻辑(插活数据与baseList有重叠)
 | 
			
		||||
        // 分离baseList:在插活数据之前的、被打断的、在插活数据之后的
 | 
			
		||||
        List<PlanSubDetailDO> beforeInsertList = new ArrayList<>();
 | 
			
		||||
        List<PlanSubDetailDO> interruptedList = new ArrayList<>();
 | 
			
		||||
        List<PlanSubDetailDO> afterInsertList = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
        if (insertStartTime != null) {
 | 
			
		||||
            for (PlanSubDetailDO baseItem : baseList) {
 | 
			
		||||
                if (baseItem.getTwoDimDate().isBefore(insertStartTime)) {
 | 
			
		||||
                    // 完全在插活数据之前
 | 
			
		||||
                    beforeInsertList.add(baseItem);
 | 
			
		||||
                } else if (baseItem.getStartTwoDimDate().isBefore(insertStartTime) &&
 | 
			
		||||
                        baseItem.getTwoDimDate().isAfter(insertStartTime.minusDays(1))) {
 | 
			
		||||
                    // 被插活数据打断
 | 
			
		||||
                    interruptedList.add(baseItem);
 | 
			
		||||
                } else {
 | 
			
		||||
                    // 在插活数据之后
 | 
			
		||||
                    afterInsertList.add(baseItem);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            beforeInsertList.addAll(baseList);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 8. 处理插活数据之前的baseList(保持原样)
 | 
			
		||||
        resultList.addAll(beforeInsertList);
 | 
			
		||||
 | 
			
		||||
        // 9. 处理被打断的baseList
 | 
			
		||||
        List<PlanSubDetailDO> remainingBaseList = new ArrayList<>();
 | 
			
		||||
        for (PlanSubDetailDO interruptedItem : interruptedList) {
 | 
			
		||||
            // 计算插活数据开始前的天数(包含节假日)
 | 
			
		||||
            long daysBeforeInsert = ChronoUnit.DAYS.between(interruptedItem.getStartTwoDimDate(), insertStartTime);
 | 
			
		||||
 | 
			
		||||
            if (daysBeforeInsert > 0) {
 | 
			
		||||
                // 创建插活前的段落
 | 
			
		||||
                LocalDateTime segmentEnd = interruptedItem.getStartTwoDimDate().plusDays(daysBeforeInsert - 1);
 | 
			
		||||
                PlanSubDetailDO beforeSegment = createBaseItem(interruptedItem, interruptedItem.getStartTwoDimDate(), segmentEnd);
 | 
			
		||||
                // designNum排除节假日
 | 
			
		||||
                beforeSegment.setDesignNum(calculateWorkDaysExcludeHolidays(interruptedItem.getStartTwoDimDate(), segmentEnd, holidayList));
 | 
			
		||||
                resultList.add(beforeSegment);
 | 
			
		||||
 | 
			
		||||
                // 保存剩余的designNum(原始designNum减去已使用的)
 | 
			
		||||
                Long usedDesignNum = calculateWorkDaysExcludeHolidays(interruptedItem.getStartTwoDimDate(), segmentEnd, holidayList);
 | 
			
		||||
                Long remainingDesignNum = interruptedItem.getDesignNum() - usedDesignNum;
 | 
			
		||||
                if (remainingDesignNum > 0) {
 | 
			
		||||
                    // 创建剩余的baseItem,稍后处理
 | 
			
		||||
                    PlanSubDetailDO remainingItem = new PlanSubDetailDO();
 | 
			
		||||
                    // 复制属性
 | 
			
		||||
                    remainingItem.setId(interruptedItem.getId());
 | 
			
		||||
                    remainingItem.setProjectPlanId(interruptedItem.getProjectPlanId());
 | 
			
		||||
                    remainingItem.setProjectId(interruptedItem.getProjectId());
 | 
			
		||||
                    remainingItem.setProjectPlanSubId(interruptedItem.getProjectPlanSubId());
 | 
			
		||||
                    remainingItem.setProjectSubCode(interruptedItem.getProjectSubCode());
 | 
			
		||||
                    remainingItem.setTwoDimOwner(interruptedItem.getTwoDimOwner());
 | 
			
		||||
                    remainingItem.setProjectSubId(interruptedItem.getProjectPlanSubId());
 | 
			
		||||
                    remainingItem.setSubType(interruptedItem.getSubType());
 | 
			
		||||
                    remainingItem.setName(interruptedItem.getName());
 | 
			
		||||
                    remainingItem.setCode(interruptedItem.getCode());
 | 
			
		||||
                    remainingItem.setDesignNum(remainingDesignNum);
 | 
			
		||||
                    remainingBaseList.add(remainingItem);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                // 插活数据与baseList有重叠或在baseList之间
 | 
			
		||||
                duringBaseListInserts.add(insertItem);
 | 
			
		||||
                // 整个被打断,全部加入剩余列表
 | 
			
		||||
                remainingBaseList.add(interruptedItem);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 6. 处理在baseList之前的插活数据:直接添加到结果列表前面
 | 
			
		||||
        beforeBaseListInserts.sort(Comparator.comparing(PlanSubDetailDO::getStartTwoDimDate));
 | 
			
		||||
        resultList.addAll(beforeBaseListInserts);
 | 
			
		||||
        // 10. 添加插活数据
 | 
			
		||||
        resultList.addAll(processedInsertList);
 | 
			
		||||
 | 
			
		||||
        // 7. 处理baseList和在其间的插活数据
 | 
			
		||||
        if (CollUtil.isEmpty(duringBaseListInserts)) {
 | 
			
		||||
            // 没有在baseList之间的插活数据,baseList保持原样
 | 
			
		||||
            resultList.addAll(baseList);
 | 
			
		||||
        } else {
 | 
			
		||||
            // 有在baseList之间的插活数据,需要重新编排
 | 
			
		||||
            // 重新构建结果列表,考虑插活数据对多个baseList的影响
 | 
			
		||||
            resultList.addAll(rebuildListWithInserts(baseList, duringBaseListInserts, holidayList));
 | 
			
		||||
        // 11. 处理剩余的baseList(被打断的剩余部分 + 原本在插活数据之后的)
 | 
			
		||||
        List<PlanSubDetailDO> allRemainingList = new ArrayList<>();
 | 
			
		||||
        allRemainingList.addAll(remainingBaseList);
 | 
			
		||||
        allRemainingList.addAll(afterInsertList);
 | 
			
		||||
 | 
			
		||||
        if (!allRemainingList.isEmpty()) {
 | 
			
		||||
            // 获取插活数据的最后结束时间
 | 
			
		||||
            LocalDateTime lastInsertEndTime = processedInsertList.stream()
 | 
			
		||||
                    .map(PlanSubDetailDO::getTwoDimDate)
 | 
			
		||||
                    .max(LocalDateTime::compareTo)
 | 
			
		||||
                    .orElse(null);
 | 
			
		||||
 | 
			
		||||
            // 从插活数据的下一天开始顺延
 | 
			
		||||
            LocalDateTime baseStartTime = lastInsertEndTime.plusDays(1);
 | 
			
		||||
 | 
			
		||||
            for (PlanSubDetailDO baseItem : allRemainingList) {
 | 
			
		||||
                // 按照原有的designNum计算新的时间段(排除节假日)
 | 
			
		||||
                LocalDateTime newStartTime = findNextNonHoliday(baseStartTime, holidayList);
 | 
			
		||||
                LocalDateTime newEndTime = calculateEndDateByWorkDays(newStartTime, baseItem.getDesignNum(), holidayList);
 | 
			
		||||
 | 
			
		||||
                // 创建新的baseItem
 | 
			
		||||
                PlanSubDetailDO newBaseItem = createBaseItem(baseItem, newStartTime, newEndTime);
 | 
			
		||||
                newBaseItem.setDesignNum(baseItem.getDesignNum()); // 保持原始designNum
 | 
			
		||||
                resultList.add(newBaseItem);
 | 
			
		||||
 | 
			
		||||
                // 更新下一个baseItem的开始时间
 | 
			
		||||
                baseStartTime = newEndTime.plusDays(1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 8. 处理在baseList之后的插活数据:直接添加到baseList后面
 | 
			
		||||
        afterBaseListInserts.sort(Comparator.comparing(PlanSubDetailDO::getStartTwoDimDate));
 | 
			
		||||
        resultList.addAll(afterBaseListInserts);
 | 
			
		||||
 | 
			
		||||
        // 9. 按开始时间排序
 | 
			
		||||
        // 12. 按开始时间排序
 | 
			
		||||
        resultList.sort(Comparator.comparing(PlanSubDetailDO::getStartTwoDimDate));
 | 
			
		||||
 | 
			
		||||
        return resultList;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 重新构建包含插活数据的列表
 | 
			
		||||
     */
 | 
			
		||||
    private List<PlanSubDetailDO> rebuildListWithInserts(List<PlanSubDetailDO> baseList,
 | 
			
		||||
                                                         List<PlanSubDetailDO> insertItems,
 | 
			
		||||
                                                         List<LocalDateTime> holidayList) {
 | 
			
		||||
        List<PlanSubDetailDO> resultList = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
        // 按时间排序插活数据
 | 
			
		||||
        insertItems.sort(Comparator.comparing(PlanSubDetailDO::getStartTwoDimDate));
 | 
			
		||||
 | 
			
		||||
        // 处理每个baseList,考虑插活数据的影响
 | 
			
		||||
        for (PlanSubDetailDO baseItem : baseList) {
 | 
			
		||||
            // 找出与这个baseItem重叠的插活数据
 | 
			
		||||
            List<PlanSubDetailDO> overlappingInserts = insertItems.stream()
 | 
			
		||||
                    .filter(insert -> !(insert.getTwoDimDate().isBefore(baseItem.getStartTwoDimDate()) ||
 | 
			
		||||
                            insert.getStartTwoDimDate().isAfter(baseItem.getTwoDimDate())))
 | 
			
		||||
                    .sorted(Comparator.comparing(PlanSubDetailDO::getStartTwoDimDate))
 | 
			
		||||
                    .collect(Collectors.toList());
 | 
			
		||||
 | 
			
		||||
            if (overlappingInserts.isEmpty()) {
 | 
			
		||||
                // 没有重叠的插活数据,保持原样
 | 
			
		||||
                resultList.add(baseItem);
 | 
			
		||||
            } else {
 | 
			
		||||
                // 有重叠的插活数据,需要分割baseItem
 | 
			
		||||
                List<PlanSubDetailDO> splitSegments = splitBaseItemByInserts(baseItem, overlappingInserts, holidayList);
 | 
			
		||||
                resultList.addAll(splitSegments);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 添加插活数据
 | 
			
		||||
        resultList.addAll(insertItems);
 | 
			
		||||
 | 
			
		||||
        return resultList;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根据插活数据分割baseItem
 | 
			
		||||
     */
 | 
			
		||||
    private List<PlanSubDetailDO> splitBaseItemByInserts(PlanSubDetailDO baseItem,
 | 
			
		||||
                                                         List<PlanSubDetailDO> overlappingInserts,
 | 
			
		||||
                                                         List<LocalDateTime> holidayList) {
 | 
			
		||||
        List<PlanSubDetailDO> segments = new ArrayList<>();
 | 
			
		||||
        LocalDateTime baseStart = baseItem.getStartTwoDimDate();
 | 
			
		||||
        LocalDateTime baseEnd = baseItem.getTwoDimDate();
 | 
			
		||||
        Long originalDesignNum = baseItem.getDesignNum();
 | 
			
		||||
 | 
			
		||||
        // 处理baseItem被插活数据分割的情况
 | 
			
		||||
        LocalDateTime currentStart = baseStart;
 | 
			
		||||
        Long usedDesignNum = 0L;
 | 
			
		||||
 | 
			
		||||
        for (PlanSubDetailDO insert : overlappingInserts) {
 | 
			
		||||
            LocalDateTime insertStart = insert.getStartTwoDimDate();
 | 
			
		||||
            LocalDateTime insertEnd = insert.getTwoDimDate();
 | 
			
		||||
 | 
			
		||||
            // 如果当前开始时间在插活数据之前,创建一个segment
 | 
			
		||||
            if (currentStart.isBefore(insertStart)) {
 | 
			
		||||
                // 计算这个时间段内可用的工作日数
 | 
			
		||||
                Long availableWorkDays = calculateWorkDaysExcludingHolidays(currentStart, insertStart.minusDays(1), holidayList);
 | 
			
		||||
                Long segmentDesignNum = Math.min(availableWorkDays, originalDesignNum - usedDesignNum);
 | 
			
		||||
 | 
			
		||||
                if (segmentDesignNum > 0) {
 | 
			
		||||
                    // 根据designNum计算实际的结束日期
 | 
			
		||||
                    LocalDateTime segmentEnd = calculateEndDateByDesignNum(currentStart, segmentDesignNum, holidayList);
 | 
			
		||||
                    PlanSubDetailDO segment = createBaseItem(baseItem, currentStart, segmentEnd, holidayList);
 | 
			
		||||
                    segment.setDesignNum(segmentDesignNum);
 | 
			
		||||
                    segments.add(segment);
 | 
			
		||||
                    usedDesignNum += segmentDesignNum;
 | 
			
		||||
                    currentStart = segmentEnd.plusDays(1);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 跳过插活数据的时间段
 | 
			
		||||
            currentStart = insertEnd.plusDays(1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 处理最后一个插活数据之后的部分
 | 
			
		||||
        if (usedDesignNum < originalDesignNum) {
 | 
			
		||||
            Long remainingDesignNum = originalDesignNum - usedDesignNum;
 | 
			
		||||
 | 
			
		||||
            // 从插活数据结束后的下一天开始,找到第一个工作日
 | 
			
		||||
            LocalDateTime segmentStart = findNextWorkDay(currentStart, holidayList);
 | 
			
		||||
 | 
			
		||||
            // 根据剩余的designNum计算结束日期(包含节假日,但designNum只计算工作日)
 | 
			
		||||
            LocalDateTime segmentEnd = calculateEndDateByDesignNumWithHolidays(segmentStart, remainingDesignNum, holidayList);
 | 
			
		||||
 | 
			
		||||
            PlanSubDetailDO segment = createBaseItem(baseItem, segmentStart, segmentEnd, holidayList);
 | 
			
		||||
            segment.setDesignNum(remainingDesignNum);
 | 
			
		||||
            segments.add(segment);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return segments;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根据designNum计算结束日期(包含节假日,但designNum只计算工作日)
 | 
			
		||||
     */
 | 
			
		||||
    private LocalDateTime calculateEndDateByDesignNumWithHolidays(LocalDateTime startDate, Long designNum, List<LocalDateTime> holidayList) {
 | 
			
		||||
        LocalDateTime current = startDate;
 | 
			
		||||
        Long workDays = 0L;
 | 
			
		||||
 | 
			
		||||
        while (workDays < designNum) {
 | 
			
		||||
            if (!isHoliday(current, holidayList)) {
 | 
			
		||||
                workDays++;
 | 
			
		||||
            }
 | 
			
		||||
            if (workDays < designNum) {
 | 
			
		||||
                current = current.plusDays(1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return current;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根据designNum计算结束日期(跳过节假日)
 | 
			
		||||
     */
 | 
			
		||||
    private LocalDateTime calculateEndDateByDesignNum(LocalDateTime startDate, Long designNum, List<LocalDateTime> holidayList) {
 | 
			
		||||
        LocalDateTime current = startDate;
 | 
			
		||||
        Long workDays = 0L;
 | 
			
		||||
 | 
			
		||||
        while (workDays < designNum) {
 | 
			
		||||
            if (!isHoliday(current, holidayList)) {
 | 
			
		||||
                workDays++;
 | 
			
		||||
            }
 | 
			
		||||
            if (workDays < designNum) {
 | 
			
		||||
                current = current.plusDays(1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return current;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 创建插活数据项(保持原有时间范围,排除节假日计算designNum)
 | 
			
		||||
     */
 | 
			
		||||
    private PlanSubDetailDO createInsertItem(PlanSubDetailDO original, LocalDateTime startDate, LocalDateTime endDate, List<LocalDateTime> holidayList) {
 | 
			
		||||
        PlanSubDetailDO insertItem = new PlanSubDetailDO();
 | 
			
		||||
        copyBaseItemProperties(insertItem, original);
 | 
			
		||||
        insertItem.setId(null); // 插活数据id为空
 | 
			
		||||
        insertItem.setIsCha("Y"); // 标记为插活
 | 
			
		||||
        insertItem.setStartTwoDimDate(startDate);
 | 
			
		||||
        insertItem.setTwoDimDate(endDate);
 | 
			
		||||
        // 计算排除节假日的工作天数
 | 
			
		||||
        insertItem.setDesignNum(calculateWorkDaysExcludingHolidays(startDate, endDate, holidayList));
 | 
			
		||||
        return insertItem;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 复制baseItem属性
 | 
			
		||||
     */
 | 
			
		||||
    private void copyBaseItemProperties(PlanSubDetailDO target, PlanSubDetailDO source) {
 | 
			
		||||
        target.setProjectPlanId(source.getProjectPlanId());
 | 
			
		||||
        target.setProjectId(source.getProjectId());
 | 
			
		||||
        target.setProjectPlanSubId(source.getProjectPlanSubId());
 | 
			
		||||
        target.setProjectSubCode(source.getProjectSubCode());
 | 
			
		||||
        target.setTwoDimOwner(source.getTwoDimOwner());
 | 
			
		||||
        target.setProjectSubId(source.getProjectSubId());
 | 
			
		||||
        target.setSubType(source.getSubType());
 | 
			
		||||
        target.setName(source.getName());
 | 
			
		||||
        target.setCode(source.getCode());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 查找下一个工作日
 | 
			
		||||
     */
 | 
			
		||||
    private LocalDateTime findNextWorkDay(LocalDateTime date, List<LocalDateTime> holidayList) {
 | 
			
		||||
        LocalDateTime current = date;
 | 
			
		||||
        while (isHoliday(current, holidayList)) {
 | 
			
		||||
            current = current.plusDays(1);
 | 
			
		||||
        }
 | 
			
		||||
        return current;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 查找下一个节假日
 | 
			
		||||
     */
 | 
			
		||||
    private LocalDateTime findNextHoliday(LocalDateTime date, List<LocalDateTime> holidayList) {
 | 
			
		||||
        return holidayList.stream()
 | 
			
		||||
                .filter(holiday -> !holiday.isBefore(date))
 | 
			
		||||
                .min(LocalDateTime::compareTo)
 | 
			
		||||
                .orElse(null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 检查是否是节假日
 | 
			
		||||
     */
 | 
			
		||||
    private boolean isHoliday(LocalDateTime date, List<LocalDateTime> holidayList) {
 | 
			
		||||
        return holidayList.contains(date.toLocalDate().atStartOfDay());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 计算排除节假日的工作天数
 | 
			
		||||
     */
 | 
			
		||||
    private Long calculateWorkDaysExcludingHolidays(LocalDateTime startDate, LocalDateTime endDate, List<LocalDateTime> holidayList) {
 | 
			
		||||
    private Long calculateWorkDaysExcludeHolidays(LocalDateTime startDate, LocalDateTime endDate, List<LocalDateTime> holidayList) {
 | 
			
		||||
        long workDays = 0;
 | 
			
		||||
        LocalDateTime current = startDate.toLocalDate().atStartOfDay();
 | 
			
		||||
        LocalDateTime end = endDate.toLocalDate().atStartOfDay();
 | 
			
		||||
 | 
			
		||||
        while (!current.isAfter(end)) {
 | 
			
		||||
            if (!isHoliday(current, holidayList)) {
 | 
			
		||||
            // 检查是否是节假日
 | 
			
		||||
            LocalDateTime finalCurrent = current;
 | 
			
		||||
            boolean isHoliday = holidayList.stream()
 | 
			
		||||
                    .anyMatch(holiday -> holiday.isEqual(finalCurrent));
 | 
			
		||||
 | 
			
		||||
            if (!isHoliday) {
 | 
			
		||||
                workDays++;
 | 
			
		||||
            }
 | 
			
		||||
            current = current.plusDays(1);
 | 
			
		||||
@ -1273,46 +1214,71 @@ public class PlanSubDetailServiceImpl implements PlanSubDetailService {
 | 
			
		||||
        return workDays;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根据工作天数计算结束日期(包含节假日)
 | 
			
		||||
     */
 | 
			
		||||
    private LocalDateTime calculateEndDateByWorkDays(LocalDateTime startDate, Long workDays, List<LocalDateTime> holidayList) {
 | 
			
		||||
        LocalDateTime current = startDate;
 | 
			
		||||
        long countedWorkDays = 0;
 | 
			
		||||
 | 
			
		||||
        while (countedWorkDays < workDays) {
 | 
			
		||||
            // 检查是否是节假日
 | 
			
		||||
            LocalDateTime finalCurrent = current;
 | 
			
		||||
            boolean isHoliday = holidayList.stream()
 | 
			
		||||
                    .anyMatch(holiday -> holiday.isEqual(finalCurrent));
 | 
			
		||||
 | 
			
		||||
            if (!isHoliday) {
 | 
			
		||||
                countedWorkDays++;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (countedWorkDays < workDays) {
 | 
			
		||||
                current = current.plusDays(1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return current;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 找到下一个非节假日日期
 | 
			
		||||
     */
 | 
			
		||||
    private LocalDateTime findNextNonHoliday(LocalDateTime startDate, List<LocalDateTime> holidayList) {
 | 
			
		||||
        LocalDateTime current = startDate;
 | 
			
		||||
 | 
			
		||||
        while (true) {
 | 
			
		||||
            LocalDateTime finalCurrent = current;
 | 
			
		||||
            boolean isHoliday = holidayList.stream()
 | 
			
		||||
                    .anyMatch(holiday -> holiday.isEqual(finalCurrent));
 | 
			
		||||
 | 
			
		||||
            if (!isHoliday) {
 | 
			
		||||
                return current;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            current = current.plusDays(1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 创建baseItem对象(用于baseList顺延)
 | 
			
		||||
     */
 | 
			
		||||
    private PlanSubDetailDO createBaseItem(PlanSubDetailDO original, LocalDateTime startDate, LocalDateTime endDate, List<LocalDateTime> holidayList) {
 | 
			
		||||
    private PlanSubDetailDO createBaseItem(PlanSubDetailDO original, LocalDateTime startDate, LocalDateTime endDate) {
 | 
			
		||||
        PlanSubDetailDO baseItem = new PlanSubDetailDO();
 | 
			
		||||
        copyBaseItemProperties(baseItem, original);
 | 
			
		||||
        // 复制原有属性
 | 
			
		||||
        baseItem.setId(original.getId());
 | 
			
		||||
        baseItem.setDesignNum(calculateWorkDaysExcludingHolidays(startDate, endDate, holidayList));
 | 
			
		||||
        baseItem.setProjectPlanId(original.getProjectPlanId());
 | 
			
		||||
        baseItem.setProjectId(original.getProjectId());
 | 
			
		||||
        baseItem.setProjectPlanSubId(original.getProjectPlanSubId());
 | 
			
		||||
        baseItem.setProjectSubCode(original.getProjectSubCode());
 | 
			
		||||
        baseItem.setTwoDimOwner(original.getTwoDimOwner());
 | 
			
		||||
        baseItem.setProjectSubId(original.getProjectPlanSubId());
 | 
			
		||||
        baseItem.setSubType(original.getSubType());
 | 
			
		||||
        baseItem.setName(original.getName());
 | 
			
		||||
        baseItem.setCode(original.getCode());
 | 
			
		||||
        baseItem.setStartTwoDimDate(startDate);
 | 
			
		||||
        baseItem.setTwoDimDate(endDate);
 | 
			
		||||
        return baseItem;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取时间段内的节假日
 | 
			
		||||
     */
 | 
			
		||||
    private List<LocalDateTime> getHolidaysInRange(LocalDateTime startDate, LocalDateTime endDate,
 | 
			
		||||
                                                   List<LocalDateTime> holidayList) {
 | 
			
		||||
        return holidayList.stream()
 | 
			
		||||
                .filter(holiday -> !holiday.isBefore(startDate) && !holiday.isAfter(endDate))
 | 
			
		||||
                .sorted()
 | 
			
		||||
                .collect(Collectors.toList());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 计算工作天数(排除节假日)
 | 
			
		||||
     */
 | 
			
		||||
    private Long calculateWorkDays(LocalDateTime startDate, LocalDateTime endDate) {
 | 
			
		||||
        long workDays = 0;
 | 
			
		||||
        LocalDateTime current = startDate.toLocalDate().atStartOfDay();
 | 
			
		||||
        LocalDateTime end = endDate.toLocalDate().atStartOfDay();
 | 
			
		||||
 | 
			
		||||
        while (!current.isAfter(end)) {
 | 
			
		||||
            workDays++;
 | 
			
		||||
            current = current.plusDays(1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return workDays;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 | 
			
		||||
@ -595,7 +595,7 @@ const saveForm = async () => {
 | 
			
		||||
    } else {
 | 
			
		||||
      message.error('失败:当前页面无数据可供保存')
 | 
			
		||||
    }
 | 
			
		||||
    reload() // 刷新列表
 | 
			
		||||
    getList()
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    message.error('失败:' + error.message)
 | 
			
		||||
  } finally {
 | 
			
		||||
@ -802,7 +802,7 @@ const handleDelete = async (row) => {
 | 
			
		||||
  try {
 | 
			
		||||
    await PlansubdetailApi.deletePlanSubDetail(row.id)
 | 
			
		||||
    message.success('删除成功')
 | 
			
		||||
    reload() // 刷新列表
 | 
			
		||||
    getList() // 刷新列表
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    console.error('删除失败:', e)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user