Commit c9ea6eb7 by 刘鑫

fix(容量): 时间片最大容量计算

1 parent 4fb7e617
......@@ -19,14 +19,15 @@ import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
......@@ -101,7 +102,7 @@ public class CapacityUtils {
}
public static List<OccupyInfoDetail> getMaxRemainBlock(LocalDateTime startTime, LocalDateTime endTime, List<OccupyInfo> occupyInfos) {
public static List<OccupyInfoDetail> getMaxRemainBlock(LocalDateTime startTime, LocalDateTime endTime, List<OccupyInfo> occupyInfos) {
//存储空闲时间段
List<OccupyInfoDetail> leisureTime = new ArrayList<>();
if (CollectionUtils.isEmpty(occupyInfos)) {
......@@ -121,7 +122,7 @@ public class CapacityUtils {
}
if (preLast.isBefore(endTime)) {
long duraionMiu = Duration.between(preLast, endTime).abs().toMinutes();
leisureTime.add(new OccupyInfoDetail(preLast,endTime, duraionMiu));
leisureTime.add(new OccupyInfoDetail(preLast, endTime, duraionMiu));
}
return leisureTime;
}
......@@ -150,6 +151,59 @@ public class CapacityUtils {
}
}
// 定义一个方法,接受一个OccupyInfoDetail列表,返回它们的交集列表
public static List<OccupyInfoDetail> intersect(List<OccupyInfoDetail> timeSlots) {
// 如果列表为空或只有一个元素,直接返回原列表
if (timeSlots == null || timeSlots.size() <= 1) {
return timeSlots;
}
// 否则,使用Stream API进行处理
return timeSlots.stream()
// 对列表中的每个元素进行两两组合,得到一个二元组列表
.flatMap(a -> timeSlots.stream().map(b -> new AbstractMap.SimpleEntry<>(a, b)))
// 过滤掉自身和重复的组合,即保证a不等于b且a在b之前
.filter(entry -> !entry.getKey().equals(entry.getValue()) && timeSlots.indexOf(entry.getKey()) < timeSlots.indexOf(entry.getValue()))
// 对每个组合计算交集,并过滤掉null值
.map(entry -> entry.getKey().intersection(entry.getValue())).filter(Objects::nonNull)
// 去除重复的交集,并收集到一个列表中
.distinct().collect(Collectors.toList());
}
/**
* 定义一个方法,接受一个OccupyInfoDetail列表,返回它们的并集列表
* 1. 将所有时间片按照开始时间从小到大排序。
* 2. 创建一个空的结果列表,用于存放合并后的时间片。
* 3. 遍历排序后的时间片列表,对于每一个时间片,执行以下操作:
* - 如果结果列表为空,或者当前时间片的开始时间大于结果列表中最后一个时间片的结束时间,说明当前时间片与结果列表中的任何一个时间片都不重叠,直接将当前时间片加入结果列表。
* - 否则,说明当前时间片与结果列表中最后一个时间片有重叠部分,将当前时间片与结果列表中最后一个时间片合并成一个新的时间片,更新结果列表中最后一个时间片为新的时间片。合并的方法是取两个时间片开始时间的较小值作为新的开始时间,取两个时间片结束时间的较大值作为新的结束时间。
* 4. 返回结果列表作为最终的并集计算结果。
*/
public static List<OccupyInfoDetail> calculateUnion(List<OccupyInfoDetail> timeSlots) {
return timeSlots.stream()
.distinct()
.sorted()
.reduce(new ArrayList<OccupyInfoDetail>(), (result, current) -> {
if (result.isEmpty()) {
result.add(current);
} else {
OccupyInfoDetail last = result.get(result.size() - 1);
if (last.getEndTime().compareTo(current.getBeginTime()) >= 0) {
result.set(result.size() - 1,
new OccupyInfoDetail(last.getBeginTime(),
last.getEndTime().compareTo(current.getEndTime()) > 0 ?
last.getEndTime() : current.getEndTime()));
} else {
result.add(current);
}
}
return result;
}, (a, b) -> a)
.stream()
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
}
public static CapacityQueryDTO.Segment caculateTargetTimeSlice(int totalTakeTime, String timeSliceName,
List<CapacityEngineerSliceUsedEntity> engineerTimeSlice,
LocalTime targetStartTime,
......@@ -159,20 +213,29 @@ public class CapacityUtils {
TimeSliceEntity timeSlice = t.getTimmeSlice();
LocalTime sliceStartHour = LocalTime.parse(timeSlice.getStart(), DateUtil.TIME_FORMATTER);
LocalTime sliceEndHour = LocalTime.parse(timeSlice.getEnd(), DateUtil.TIME_FORMATTER);
return (targetStartTime.isAfter(sliceStartHour) && targetEndTime.isBefore(sliceEndHour)) ||
(targetStartTime.equals(sliceStartHour) || targetEndTime.equals(sliceEndHour));
return !(targetStartTime.isAfter(sliceEndHour) || targetEndTime.isBefore(sliceStartHour));
}).collect(Collectors.toList());
CapacityQueryDTO.Segment segment = new CapacityQueryDTO.Segment();
segment.setName(timeSliceName);
segment.setEndTime(DateUtil.toDate(LocalDateTime.of(targetDate, targetEndTime)));
segment.setBeginTime(DateUtil.toDate(LocalDateTime.of(targetDate, targetStartTime)));
if (!org.apache.commons.collections4.CollectionUtils.isEmpty(collect)) {
OptionalLong optionalLong = collect.stream().mapToLong(t -> {
Long maxDuration = t.getMaxDuration();
return Objects.isNull(maxDuration) ? 0L : maxDuration;
}).max();
long maxDuration = optionalLong.isEmpty() ? 0 : optionalLong.getAsLong();
if (!CollectionUtils.isEmpty(collect)) {
//时间区间范围空闲时间段
//按人分组
Map<String, List<CapacityEngineerSliceUsedEntity>> engineerSliceMap = collect.stream()
.collect(Collectors.groupingBy(CapacityEngineerSliceUsedEntity::getEngineerCode));
//计算最大空闲时间
List<Long> engineerMaxDurationList = new ArrayList<>();
engineerSliceMap.forEach((engineerCode, t) -> {
long engineerMaxDuration = t.stream().map(CapacityEngineerSliceUsedEntity::getDurationTime)
.flatMap(Collection::stream)
.mapToLong(OccupyInfoDetail::getDuration)
.sum();
engineerMaxDurationList.add(engineerMaxDuration);
});
long maxDuration = Collections.max(engineerMaxDurationList);
segment.setMaxDuration(maxDuration);
long remain = collect.stream().mapToLong(CapacityEngineerSliceUsedEntity::getCapLeft).sum();
segment.setRemain(remain);
......
......@@ -6,12 +6,14 @@ import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@NoArgsConstructor
public class OccupyInfoDetail extends OccupyInfo {
public class OccupyInfoDetail extends OccupyInfo implements Comparable<OccupyInfoDetail> {
/**
* 时间片时长(分钟)
*/
......@@ -21,4 +23,28 @@ public class OccupyInfoDetail extends OccupyInfo {
super(beginTime, endTime);
this.duration = duration;
}
public OccupyInfoDetail(LocalDateTime beginTime, LocalDateTime endTime) {
super(beginTime, endTime);
}
// 判断两个时间片是否有交集
public boolean overlaps(OccupyInfoDetail other) {
// 如果一个时间片的开始时间在另一个时间片的结束时间之后,或者一个时间片的结束时间在另一个时间片的开始时间之前,那么他们没有交集
return !(this.beginTime.isAfter(other.endTime) || this.endTime.isBefore(other.beginTime));
}
// 计算两个时间片的交集
public OccupyInfoDetail intersection(OccupyInfoDetail other) {
// 如果两个时间片没有交集,返回null
if (!this.overlaps(other)) {
return null;
}
// 否则,返回一个新的时间片,其开始时间是两个时间片中较晚的开始时间,其结束时间是两个时间片中较早的结束时间
return new OccupyInfoDetail(this.beginTime.isAfter(other.beginTime) ? this.beginTime : other.beginTime, this.endTime.isBefore(other.endTime) ? this.endTime : other.endTime);
}
@Override
public int compareTo(OccupyInfoDetail o) {
return this.beginTime.compareTo(o.beginTime);
}
}
package com.dituhui.pea.order.common;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
@ExtendWith(MockitoExtension.class)
public class CapacityUtilsTest {
@Test
public void testMaxRemainBlock() {
LocalDateTime sliceStart = LocalDateTime.of(LocalDate.now(), LocalTime.of(8, 0));
LocalDateTime sliceEnd = LocalDateTime.of(LocalDate.now(), LocalTime.of(9, 0));
OccupyInfo occupyInfo = new OccupyInfo()
.setBeginTime(LocalDateTime.of(LocalDate.now(), LocalTime.of(8, 0)))
.setEndTime(LocalDateTime.of(LocalDate.now(), LocalTime.of(8, 25)));
ArrayList<OccupyInfo> occupyInfos = new ArrayList<>();
occupyInfos.add(occupyInfo);
List<OccupyInfoDetail> remainBlock = CapacityUtils.getMaxRemainBlock(sliceStart, sliceEnd, occupyInfos);
Assertions.assertEquals(35, remainBlock.stream().mapToLong(OccupyInfoDetail::getDuration).max().orElse(Duration.between(sliceStart, sliceEnd).abs().toMinutes()));
occupyInfos.clear();
occupyInfos.add(new OccupyInfo()
.setBeginTime(LocalDateTime.of(LocalDate.now(), LocalTime.of(8, 10)))
.setEndTime(LocalDateTime.of(LocalDate.now(), LocalTime.of(8, 25))));
remainBlock = CapacityUtils.getMaxRemainBlock(sliceStart, sliceEnd, occupyInfos);
Assertions.assertEquals(35, remainBlock.stream().mapToLong(OccupyInfoDetail::getDuration).max().orElse(Duration.between(sliceStart, sliceEnd).abs().toMinutes()));
occupyInfos.clear();
occupyInfos.add(new OccupyInfo()
.setBeginTime(LocalDateTime.of(LocalDate.now(), LocalTime.of(8, 20)))
.setEndTime(LocalDateTime.of(LocalDate.now(), LocalTime.of(8, 40))));
remainBlock = CapacityUtils.getMaxRemainBlock(sliceStart, sliceEnd, occupyInfos);
Assertions.assertEquals(20, remainBlock.stream().mapToLong(OccupyInfoDetail::getDuration).max().orElse(Duration.between(sliceStart, sliceEnd).abs().toMinutes()));
}
@Test
public void calculateUnionTest() {
//三个时间片[9:00, 10:00],[9:30, 11:00]和[10:30, 12:00],那么它们的并集计算结果是[9:00, 12:00]
List<OccupyInfoDetail> occupyInfoDetails = new ArrayList<>();
occupyInfoDetails.add(
new OccupyInfoDetail(LocalDateTime.of(LocalDate.now(), LocalTime.of(9, 0)
), LocalDateTime.of(LocalDate.now(), LocalTime.of(10, 0))));
occupyInfoDetails.add(
new OccupyInfoDetail(LocalDateTime.of(LocalDate.now(), LocalTime.of(9, 30)
), LocalDateTime.of(LocalDate.now(), LocalTime.of(11, 0))));
occupyInfoDetails.add(
new OccupyInfoDetail(LocalDateTime.of(LocalDate.now(), LocalTime.of(10, 30)
), LocalDateTime.of(LocalDate.now(), LocalTime.of(12, 0))));
List<OccupyInfoDetail> occupyInfoDetails1 = CapacityUtils.calculateUnion(occupyInfoDetails);
Assertions.assertEquals(1, occupyInfoDetails1.size());
OccupyInfoDetail unionOccupyInfo = occupyInfoDetails1.get(0);
Assertions.assertEquals(LocalDateTime.of(LocalDate.now(), LocalTime.of(12, 0)), unionOccupyInfo.getEndTime());
Assertions.assertEquals(LocalDateTime.of(LocalDate.now(), LocalTime.of(9, 0)), unionOccupyInfo.getBeginTime());
occupyInfoDetails.clear();
occupyInfoDetails.add(
new OccupyInfoDetail(LocalDateTime.of(LocalDate.now(), LocalTime.of(9, 0)
), LocalDateTime.of(LocalDate.now(), LocalTime.of(10, 0))));
occupyInfoDetails.add(
new OccupyInfoDetail(LocalDateTime.of(LocalDate.now(), LocalTime.of(9, 30)
), LocalDateTime.of(LocalDate.now(), LocalTime.of(11, 0))));
occupyInfoDetails.add(
new OccupyInfoDetail(LocalDateTime.of(LocalDate.now(), LocalTime.of(11, 30)
), LocalDateTime.of(LocalDate.now(), LocalTime.of(12, 0))));
occupyInfoDetails1 = CapacityUtils.calculateUnion(occupyInfoDetails);
Assertions.assertEquals(2, occupyInfoDetails1.size());
}
}
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!