Commit df5ab310 by 刘鑫

Merge branch 'develop-16542' into 'develop'

Develop 16542

See merge request !351
2 parents ee922485 d9c9d3ed
package com.dituhui.pea.order.common;
import com.dituhui.pea.order.common.jackson.DateUtil;
import com.dituhui.pea.order.dao.MapBlockInfoDao;
import com.dituhui.pea.order.dao.SkillInfoDao;
import com.dituhui.pea.order.dto.param.CapacityQueryDTO;
import com.dituhui.pea.order.entity.CapacityEngineerSliceUsedEntity;
import com.dituhui.pea.order.entity.MapBlockInfoEntity;
import com.dituhui.pea.order.entity.SkillInfoEntity;
import com.dituhui.pea.order.entity.TimeSliceEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.OptionalLong;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.Collectors;
@Component
......@@ -58,4 +70,64 @@ public class CapacityUtils {
return layers;
}
public static List<CapacityQueryDTO.Segment> getEngineerTypeDay(List<TimeSliceEntity> typeTimeSlice,
List<CapacityEngineerSliceUsedEntity> engineerTimeSlice,
LocalDate date, int totalTakeTime) {
ForkJoinPool forkJoinPool = new ForkJoinPool(Runtime.getRuntime().availableProcessors());
//容量
ForkJoinTask<List<CapacityQueryDTO.Segment>> fork = forkJoinPool.submit(() -> typeTimeSlice.parallelStream()
.map(targetTimeSlice -> {
final LocalTime targetStartTime = LocalTime.parse(targetTimeSlice.getStart(), DateUtil.TIME_FORMATTER);
final LocalTime targetEndTime = LocalTime.parse(targetTimeSlice.getEnd(), DateUtil.TIME_FORMATTER);
return caculateTargetTimeSlice(totalTakeTime, targetTimeSlice.getName(),
engineerTimeSlice, targetStartTime, targetEndTime, date);
}).collect(Collectors.toList())).fork();
try {
return fork.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
} finally {
forkJoinPool.shutdown();
}
}
public static CapacityQueryDTO.Segment caculateTargetTimeSlice(int totalTakeTime, String timeSliceName,
List<CapacityEngineerSliceUsedEntity> engineerTimeSlice,
LocalTime targetStartTime,
LocalTime targetEndTime, LocalDate targetDate) {
List<CapacityEngineerSliceUsedEntity> collect = engineerTimeSlice.parallelStream().filter(t -> {
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));
}).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();
segment.setMaxDuration(maxDuration);
long remain = collect.stream().mapToLong(CapacityEngineerSliceUsedEntity::getCapLeft).sum();
segment.setRemain(remain);
segment.setStatus(totalTakeTime <= maxDuration ? 1 : 0);
} else {
segment.setMaxDuration(0);
segment.setRemain(0);
segment.setStatus(0);
}
return segment;
}
}
......@@ -31,7 +31,7 @@ public class DateSplit {
}
public LocalDateTime getLocalEndDateTime() {
return DateUtil.fromDate(this.startDateTime);
return DateUtil.fromDate(this.endDateTime);
}
/**
......
......@@ -33,6 +33,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
......@@ -123,7 +124,12 @@ public class CalcEngineerCapacityScheduler {
final TimeSliceEntity timeSlice = sliceCap.getTimmeSlice();
LocalTime sliceStartLocalTime = LocalTime.parse(timeSlice.getStart(), DateUtil.TIME_FORMATTER);
LocalTime sliceEndLocalTime = LocalTime.parse(timeSlice.getEnd(), DateUtil.TIME_FORMATTER);
long lengthOfLeave = sliceCap.getCapTotal();
if (Objects.isNull(sliceCap.getCapTotal()) || 0 == sliceCap.getCapTotal()) {
log.info("-----------------》工程师{}的时间片总容量为0, 跳过容量计算", engineerCode);
continue;
}
long totalUseTime = 0;
long maxDuration = 0;
for (OrderInfoEntity order : orders) {
LocalTime planStartTime = order.getPlanStartTime().toLocalTime();
LocalTime planEndTime = order.getPlanEndTime().toLocalTime();
......@@ -135,22 +141,26 @@ public class CalcEngineerCapacityScheduler {
boolean contains = ss.contains(order.getOrderStatus());
//请假时间仅落在当前时间段内, 当前时间段请假时长为 请假结束时间- 请假开始时间
if (startIn && DateTimeUtil.isIn(planEndTime, sliceStartLocalTime, sliceEndLocalTime)) {
lengthOfLeave = contains
? lengthOfLeave - DateTimeUtil.betweenTwoTime(planStartTime, planEndTime, TimeUnit.MINUTES) :
lengthOfLeave + DateTimeUtil.betweenTwoTime(planStartTime, planEndTime, TimeUnit.MINUTES);
long useTime = DateTimeUtil.betweenTwoTime(planStartTime, planEndTime, TimeUnit.MINUTES);
totalUseTime = contains
? totalUseTime + useTime : totalUseTime - useTime;
} else if (startIn && endAfter) {
//落在当前时间段和下一个时间段
lengthOfLeave = contains
? lengthOfLeave - DateTimeUtil.betweenTwoTime(planStartTime, sliceEndLocalTime, TimeUnit.MINUTES)
: lengthOfLeave +DateTimeUtil.betweenTwoTime(planStartTime, sliceEndLocalTime, TimeUnit.MINUTES);
long useTime = DateTimeUtil.betweenTwoTime(planStartTime, sliceEndLocalTime, TimeUnit.MINUTES);
totalUseTime = contains
? totalUseTime + useTime : totalUseTime - useTime;
} else if (planStartTime.isBefore(sliceStartLocalTime) && endAfter) {
lengthOfLeave = contains
? lengthOfLeave - DateTimeUtil.betweenTwoTime(sliceStartLocalTime, sliceEndLocalTime, TimeUnit.MINUTES):
lengthOfLeave + DateTimeUtil.betweenTwoTime(sliceStartLocalTime, sliceEndLocalTime, TimeUnit.MINUTES);
long usedTime = DateTimeUtil.betweenTwoTime(sliceStartLocalTime, sliceEndLocalTime, TimeUnit.MINUTES);
totalUseTime = contains
? totalUseTime + usedTime : totalUseTime - usedTime;
}
}
sliceCap.setCapLeft(lengthOfLeave);
if (totalUseTime < 0) {
totalUseTime = 0;
}
sliceCap.setCapLeft(sliceCap.getCapTotal() - totalUseTime);
sliceCap.setMaxDuration(sliceCap.getCapTotal() - totalUseTime);
}
engineerSliceUsedCapacityDao.saveAll(engineerTimeSlice);
}
......
......@@ -32,7 +32,10 @@ import com.dituhui.pea.order.feign.ISaaSRemoteService;
import com.dituhui.pea.order.service.CapacityQueryService;
import com.dituhui.pea.pojo.fendan.FendanDTO;
import com.google.common.collect.Sets;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
......@@ -44,11 +47,17 @@ import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.OptionalLong;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
......@@ -83,7 +92,8 @@ public class CapacityQueryServiceImpl implements CapacityQueryService {
private EngineerSliceUsedCapacityDao engineerSliceUsedCapacityDao;
@Override
public Result<CapacityQueryDTO.Result> matchCapacityData(List<CapacityQueryDTO.Service> services, Location location, LocalDate beginDate, LocalDate endDate) {
public Result<CapacityQueryDTO.Result> matchCapacityData(List<CapacityQueryDTO.Service> services, Location location,
LocalDate beginDate, LocalDate endDate) {
//1:根据经纬度分单获取面
FendanDTO fendanDTO = new FendanDTO();
......@@ -125,92 +135,48 @@ public class CapacityQueryServiceImpl implements CapacityQueryService {
.mapToInt(SkillInfoEntity::getTakeTime)
.sum();
//查询单个工程师日期范围内的技能容量信息 后加和汇总
List<TimeSliceEntity> halfDayTypeTimeSlice = timeSliceDao.findByType("HALF_DAY");
List<TimeSliceEntity> allDayTypeTimeSlice = timeSliceDao.findByType("ALL_DAY");
CopyOnWriteArrayList<TimeSliceEntity> halfDayTypeTimeSlice = new CopyOnWriteArrayList<>(timeSliceDao.findByType("HALF_DAY"));
CopyOnWriteArrayList<TimeSliceEntity> allDayTypeTimeSlice = new CopyOnWriteArrayList<>(timeSliceDao.findByType("ALL_DAY"));
CopyOnWriteArrayList<TimeSliceEntity> timeSliceEntities = new CopyOnWriteArrayList<>(sliceTime(LocalDate.now(),
LocalTime.of(8, 0),
LocalTime.of(18, 0)));
LocalDate currentDate = beginDate;
//定义返回值
CapacityQueryDTO.Result result = new CapacityQueryDTO.Result();
result.setGroupId(groupId);
result.setTakeTime(totalTakeTime);
List<CapacityQueryDTO.Data> datas = new ArrayList<>();
while (!currentDate.isAfter(endDate)) {
List<CapacityEngineerSliceUsedEntity> allEngineerTimeSlice = new ArrayList<>();
for (EngineerInfoEntity engineerInfo : allFulfillEngineer) {
List<CapacityEngineerSliceUsedEntity> engineerTimeSlice = engineerSliceUsedCapacityDao
.findByWorkdayAndEngineerCode(DateTimeUtil.formatDate(currentDate), engineerInfo.getEngineerCode());
allEngineerTimeSlice.addAll(engineerTimeSlice);
}
//计算所有工程师半天类型容量
List<CapacityQueryDTO.Segment> engineerHalfDay = getEngineerTypeDay(halfDayTypeTimeSlice,
allEngineerTimeSlice, currentDate, totalTakeTime);
//计算所有工程师全天天类型容量
List<CapacityQueryDTO.Segment> engineerAllDay = getEngineerTypeDay(allDayTypeTimeSlice,
allEngineerTimeSlice, currentDate, totalTakeTime);
//TODO 时间段
CapacityQueryDTO.Data data = new CapacityQueryDTO.Data();
data.setDate(DateUtil.toDate(currentDate));
List<CapacityQueryDTO.Segment> objects = new ArrayList<>();
objects.addAll(engineerHalfDay);
objects.addAll(engineerAllDay);
data.setSegments(objects);
datas.add(data);
final int corePoolSize = Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(corePoolSize, corePoolSize, 1, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(16));
Semaphore semaphore = new Semaphore(corePoolSize);
List<Future<CapacityQueryDTO.Data>> futureDatas = new ArrayList<>();
while (!currentDate.isAfter(endDate)) {
log.info("【matchCapacityData】----------->开始计算{}的容量", currentDate);
Task task = new Task(allFulfillEngineer, currentDate, halfDayTypeTimeSlice,
totalTakeTime, allDayTypeTimeSlice, timeSliceEntities, semaphore);
Future<CapacityQueryDTO.Data> dataFuture = poolExecutor.submit(task);
futureDatas.add(dataFuture);
currentDate = currentDate.plusDays(1);
}
List<CapacityQueryDTO.Data> datas = new ArrayList<>();
for (Future<CapacityQueryDTO.Data> dataFuture : futureDatas) {
try {
CapacityQueryDTO.Data data = dataFuture.get();
if (Objects.nonNull(data)) {
datas.add(data);
}
} catch (InterruptedException | ExecutionException e) {
log.error("获取容量计算结果失败------------------------------------>{}", e.getMessage(), e);
}
}
result.setDatas(datas);
return Result.success(result);
}
private List<CapacityQueryDTO.Segment> getEngineerTypeDay(List<TimeSliceEntity> typeTimeSlice,
List<CapacityEngineerSliceUsedEntity> engineerTimeSlice,
LocalDate date, int totalTakeTime) {
ArrayList<CapacityQueryDTO.Segment> segments = new ArrayList<>();
//半天容量
for (TimeSliceEntity targetTimeSlice : typeTimeSlice) {
final LocalTime targetStartTime = LocalTime.parse(targetTimeSlice.getStart(), DateUtil.TIME_FORMATTER);
final LocalTime targetEndTime = LocalTime.parse(targetTimeSlice.getEnd(), DateUtil.TIME_FORMATTER);
CapacityQueryDTO.Segment segment = caculateTargetTimeSlice(totalTakeTime, targetTimeSlice.getName(),
engineerTimeSlice, targetStartTime, targetEndTime, date);
segments.add(segment);
}
return segments;
}
private static CapacityQueryDTO.Segment caculateTargetTimeSlice(int totalTakeTime, String timeSliceName,
List<CapacityEngineerSliceUsedEntity> engineerTimeSlice, LocalTime targetStartTime,
LocalTime targetEndTime, LocalDate targetDate) {
List<CapacityEngineerSliceUsedEntity> collect = engineerTimeSlice.stream().filter(t -> {
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));
}).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 (!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();
segment.setMaxDuration(maxDuration);
long remain = collect.stream().mapToLong(CapacityEngineerSliceUsedEntity::getCapLeft).sum();
segment.setRemain(remain);
segment.setStatus(totalTakeTime <= maxDuration ? 1 : 0);
} else {
segment.setMaxDuration(0);
segment.setRemain(0);
segment.setStatus(0);
}
return segment;
}
@Override
public CapacityQueryDTO.Segment queryCapacityByTeam(String teamId, CapacityQueryDTO.Service service,
......@@ -223,7 +189,7 @@ public class CapacityQueryServiceImpl implements CapacityQueryService {
SkillInfoEntity skillInfo = skillInfoDao.getByBrandAndTypeAndSkill(service.getBrand(),
service.getProductType(), service.getServiceType());
List<CapacityEngineerSliceUsedEntity> allEngineerTimeSlice = new ArrayList<>();
List<CapacityEngineerSliceUsedEntity> allEngineerTimeSlice = new CopyOnWriteArrayList<>();
for (EngineerInfoEntity engineerInfo : engineerInfoEntities) {
List<CapacityEngineerSliceUsedEntity> engineerTimeSlice = engineerSliceUsedCapacityDao
.findByWorkdayAndEngineerCode(DateTimeUtil.formatDate(targetDate), engineerInfo.getEngineerCode());
......@@ -231,7 +197,7 @@ public class CapacityQueryServiceImpl implements CapacityQueryService {
}
log.info("[matchCapacityData]【符合技能要求的工程师总数为:{} 个】", engineerInfoEntities.size());
return caculateTargetTimeSlice(skillInfo.getTakeTime(), "时间段", allEngineerTimeSlice, startTime, endTime, targetDate);
return CapacityUtils.caculateTargetTimeSlice(skillInfo.getTakeTime(), "时间段", allEngineerTimeSlice, startTime, endTime, targetDate);
}
@Override
......@@ -260,7 +226,8 @@ public class CapacityQueryServiceImpl implements CapacityQueryService {
SkillInfoEntity skillInfo = skillInfoDao.getByBrandAndTypeAndSkill(service.getBrand(),
service.getProductType(), service.getServiceType());
return caculateTargetTimeSlice(skillInfo.getTakeTime(), "时间段", engineerTimeSlice, startTime, endTime, date);
return CapacityUtils.caculateTargetTimeSlice(skillInfo.getTakeTime(), "时间段",
new CopyOnWriteArrayList<>(engineerTimeSlice), startTime, endTime, date);
}
@Override
......@@ -337,6 +304,64 @@ public class CapacityQueryServiceImpl implements CapacityQueryService {
private String timeEnd;
}
@Setter
@Getter
@AllArgsConstructor
class Task implements Callable<CapacityQueryDTO.Data> {
private Set<EngineerInfoEntity> allFulfillEngineer;
private LocalDate currentDate;
private CopyOnWriteArrayList<TimeSliceEntity> halfDayTypeTimeSlice;
private int totalTakeTime;
private CopyOnWriteArrayList<TimeSliceEntity> allDayTypeTimeSlice;
private CopyOnWriteArrayList<TimeSliceEntity> timeSliceEntities;
private Semaphore semaphore;
@Override
public CapacityQueryDTO.Data call() throws Exception {
CapacityQueryDTO.Data data = null;
try {
semaphore.acquire();
data = getData(allFulfillEngineer, currentDate, halfDayTypeTimeSlice,
totalTakeTime, allDayTypeTimeSlice, timeSliceEntities);
} catch (InterruptedException e) {
log.error("【计算日期为:{} 的容量失败】Task: 失败原因{}", currentDate, e.getMessage());
} finally {
semaphore.release();
}
return data;
}
public CapacityQueryDTO.Data getData(Set<EngineerInfoEntity> allFulfillEngineer, LocalDate currentDate,
List<TimeSliceEntity> halfDayTypeTimeSlice, int totalTakeTime,
List<TimeSliceEntity> allDayTypeTimeSlice,
List<TimeSliceEntity> timeSliceEntities) {
List<CapacityEngineerSliceUsedEntity> allEngineerTimeSlice = new CopyOnWriteArrayList<>();
for (EngineerInfoEntity engineerInfo : allFulfillEngineer) {
List<CapacityEngineerSliceUsedEntity> engineerTimeSlice = engineerSliceUsedCapacityDao
.findByWorkdayAndEngineerCode(DateTimeUtil.formatDate(currentDate), engineerInfo.getEngineerCode());
allEngineerTimeSlice.addAll(engineerTimeSlice);
}
//计算所有工程师半天类型容量
List<CapacityQueryDTO.Segment> engineerHalfDay = CapacityUtils.getEngineerTypeDay(halfDayTypeTimeSlice,
allEngineerTimeSlice, currentDate, totalTakeTime);
//计算所有工程师全天天类型容量
List<CapacityQueryDTO.Segment> engineerAllDay = CapacityUtils.getEngineerTypeDay(allDayTypeTimeSlice,
allEngineerTimeSlice, currentDate, totalTakeTime);
//计算所有工程师时间段类型容量
/*List<CapacityQueryDTO.Segment> timeSliceTwoHour = CapacityUtils.getEngineerTypeDay(timeSliceEntities,
allEngineerTimeSlice, currentDate, totalTakeTime);*/
CapacityQueryDTO.Data data = new CapacityQueryDTO.Data();
data.setDate(DateUtil.toDate(currentDate));
List<CapacityQueryDTO.Segment> objects = new ArrayList<>();
objects.addAll(engineerHalfDay);
objects.addAll(engineerAllDay);
/*objects.addAll(timeSliceTwoHour);*/
data.setSegments(objects);
return data;
}
}
private SpanInfo getTimeSpanInfo(String teamId, String date, String timeSpan, String timeSpanDetail) {
// 如果是"全天",直接返回标题和2个时间点;否则,解析timeSpanDetail中的数据返回
OrgTeamEntity orgTeamEntity = orgTeamDao.getByTeamId(teamId);
......@@ -414,7 +439,15 @@ public class CapacityQueryServiceImpl implements CapacityQueryService {
LocalDateTime start = LocalDateTime.of(date, startTime);
LocalDateTime end = LocalDateTime.of(date, endTime);
List<DateSplit> dateSplits = DateUtil.splitByHour(DateUtil.toDate(start), DateUtil.toDate(end), 2, 1);
return Collections.emptyList();
return dateSplits.stream()
.map(dateSplice -> {
//localtime 转string
TimeSliceEntity timeSliceEntity = new TimeSliceEntity();
timeSliceEntity.setStart(DateTimeUtil.TIME_FORMAT.format(dateSplice.getStartTime()));
timeSliceEntity.setEnd(DateTimeUtil.TIME_FORMAT.format(dateSplice.getEndTime()));
timeSliceEntity.setName("时间段");
return timeSliceEntity;
}).collect(Collectors.toList());
}
}
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!