Commit ae692a05 by 刘鑫

Merge branch 'develop-16542' into 'develop'

Develop 16542

See merge request !365
2 parents a83361a3 aae7ca0e
......@@ -4,7 +4,6 @@ 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.CapacityEngineerCalendarEntity;
import com.dituhui.pea.order.entity.CapacityEngineerSliceUsedEntity;
import com.dituhui.pea.order.entity.MapBlockInfoEntity;
import com.dituhui.pea.order.entity.OrderInfoEntity;
......@@ -24,10 +23,12 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
......@@ -45,7 +46,14 @@ public class CapacityUtils {
private MapBlockInfoDao mapBlockInfoDao;
public List<String> getTeamIdsByBlockIdsAndLayerIds(List<String> blockIds, List<String> layerIds) {
List<String> teamIds = mapBlockInfoDao.findByAreaIdsInAndLayerIdIn(blockIds, layerIds).stream().map(MapBlockInfoEntity::getTeamId).collect(Collectors.toList());
Set<MapBlockInfoEntity> mapBlockInBlockIdsList = new HashSet<>();
//区划所在范围所有工作队
for (String blockId : blockIds) {
List<MapBlockInfoEntity> mapBlockInfoEntities = mapBlockInfoDao.findByAreaIdsLikeAndLayerIdIn("%" + blockId + "%", layerIds);
mapBlockInBlockIdsList.addAll(mapBlockInfoEntities);
}
List<String> teamIds = mapBlockInBlockIdsList.stream().map(MapBlockInfoEntity::getTeamId).collect(Collectors.toList());
log.info("根据输入信息返回对应的可能的工作队列表({}/{}) ===> {}", blockIds, layerIds, teamIds);
return teamIds;
}
......@@ -81,13 +89,13 @@ public class CapacityUtils {
//根据capacity_engineer_calendar和order_info,来确定当天剩下的最大连续时间区块 和已使用容量;
public static List<OccupyInfo> caculate(final LocalDateTime startTime, final LocalDateTime endTime, List<OrderInfoEntity> orders,
List<CapacityEngineerCalendarEntity> configs) {
List<OccupyInfoDetail> configs) {
//工作日历使用时间片
List<OccupyInfo> calendar = Optional.ofNullable(configs).orElse(Collections.emptyList())
.stream()
.filter(t -> DateUtil.checkTimesHasOverlap(t.getStartTime(), t.getEndTime(), startTime, endTime))
.map(e -> DateUtil.timesOverlap(e.getStartTime(), e.getEndTime(), startTime, endTime))
.filter(t -> DateUtil.checkTimesHasOverlap(t.getBeginTime(), t.getEndTime(), startTime, endTime))
.map(e -> DateUtil.timesOverlap(e.getBeginTime(), e.getEndTime(), startTime, endTime))
.collect(Collectors.toList());
// 工单已使用的时间片
List<OccupyInfo> order = Optional.ofNullable(orders).orElse(Collections.emptyList())
......
......@@ -12,7 +12,7 @@ import java.util.List;
@Repository
public interface MapBlockInfoDao extends JpaRepository<MapBlockInfoEntity, Integer>, JpaSpecificationExecutor<MapBlockInfoEntity> {
List<MapBlockInfoEntity> findByAreaIdsInAndLayerIdIn(List<String> blockIds, List<String> layerIds);
List<MapBlockInfoEntity> findByAreaIdsLikeAndLayerIdIn(String blockId, List<String> layerIds);
MapBlockInfoEntity findByAreaIds(String blockId);
......
......@@ -11,6 +11,7 @@ import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Set;
@Repository
@Where(clause = "status = 1")
......@@ -43,4 +44,13 @@ public interface OrgTeamDao extends JpaRepository<OrgTeamEntity, Integer>, JpaSp
void updateStatusByTeamId(String teamId, int status);
public List<OrgTeamEntity> findByTeamIdIn(List<String> ids);
/**
* 获取工程师所在的工作队
*
* @param engineerCode 工程师代码
* @return 工程师所在工作队信息
*/
@Query(value = "SELECT ot.* FROM org_team ot LEFT JOIN org_team_engineer ote ON ote.team_id = ot.team_id WHERE ote.engineer_code = :engineerCode", nativeQuery = true)
Set<OrgTeamEntity> selectTeamByEngineerCode(String engineerCode);
}
......@@ -20,6 +20,7 @@ import com.dituhui.pea.order.entity.EngineerBusinessEntity;
import com.dituhui.pea.order.entity.EngineerInfoEntity;
import com.dituhui.pea.order.entity.OrderInfoEntity;
import com.dituhui.pea.order.entity.TimeSliceEntity;
import com.dituhui.pea.order.service.EngineerCalendarService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
......@@ -62,6 +63,8 @@ public class CalcEngineerCapacityScheduler {
private TimeSliceDao timeSliceDao;
@Autowired
private EngineerSliceUsedCapacityDao engineerSliceUsedCapacityDao;
@Autowired
private EngineerCalendarService engineerCalendarService;
@Scheduled(cron = "${scheduler.calc-engineer-capacity.cron-expr}")
public void run() {
......@@ -96,7 +99,7 @@ public class CalcEngineerCapacityScheduler {
EngineerBusinessEntity businessEntity = engineerBusinessDao.getByEngineerCode(engineerCode);
// 就算单小时时间段工程师技能已用容量存储至时间切片工程师时间表内
//添加工程师日历
List<CapacityEngineerCalendarEntity> configs = capacityEngineerCalendarDao.findCalendarByWorkdayAndEngineerCode(date, engineerCode);
List<OccupyInfoDetail> configs = engineerCalendarService.getEngineerWorkDayCalendar(engineerCode, LocalDate.parse(date, DateTimeUtil.DATE_FORMAT));
//剔除重排和取消单
orders = Optional.ofNullable(orders).orElse(Collections.emptyList())
.stream()
......@@ -124,7 +127,7 @@ public class CalcEngineerCapacityScheduler {
}
//添加工程师日历参数
private void initOneEngineerTimeSlot(String date, String engineerCode, List<CapacityEngineerCalendarEntity> configs,
private void initOneEngineerTimeSlot(String date, String engineerCode, List<OccupyInfoDetail> configs,
List<OrderInfoEntity> orders) {
final LocalDate localDate = LocalDate.parse(date, DateTimeUtil.DATE_FORMAT);
......
......@@ -2,6 +2,7 @@ package com.dituhui.pea.order.scheduler;
import cn.hutool.core.collection.CollectionUtil;
import com.dituhui.pea.order.common.DateUtils;
import com.dituhui.pea.order.common.OccupyInfoDetail;
import com.dituhui.pea.order.common.jackson.DateTimeUtil;
import com.dituhui.pea.order.common.jackson.DateUtil;
import com.dituhui.pea.order.dao.CapacityEngineerCalendarDao;
......@@ -15,6 +16,7 @@ import com.dituhui.pea.order.entity.CapacityEngineerStatEntity;
import com.dituhui.pea.order.entity.EngineerBusinessEntity;
import com.dituhui.pea.order.entity.EngineerInfoEntity;
import com.dituhui.pea.order.entity.TimeSliceEntity;
import com.dituhui.pea.order.service.EngineerCalendarService;
import com.dituhui.pea.order.service.EngineerSliceUsedCapacityService;
import lombok.Data;
import lombok.experimental.Accessors;
......@@ -62,6 +64,8 @@ public class InitEngineerCapacityScheduler {
private TimeSliceDao timeSliceDao;
@Autowired
private EngineerSliceUsedCapacityService EngineerSliceUsedCapacityService;
@Autowired
private EngineerCalendarService engineerCalendarService;
private boolean verifyCalendar(List<CapacityEngineerCalendarEntity> configs) {
// 检查多条请假配置是否有交叉行为; configs已经根据startTime排序
......@@ -80,11 +84,11 @@ public class InitEngineerCapacityScheduler {
return true;
}
private long sumLeaveTime(List<CapacityEngineerCalendarEntity> configs) {
return configs.stream().mapToLong(e -> Duration.between(e.getStartTime(), e.getEndTime()).toMinutes()).sum();
private long sumLeaveTime(List<OccupyInfoDetail> configs) {
return configs.stream().mapToLong(e -> Duration.between(e.getBeginTime(), e.getEndTime()).toMinutes()).sum();
}
private CapacityStats calculateWorkTime(String date, String engineerCode, List<CapacityEngineerCalendarEntity> configs) {
private CapacityStats calculateWorkTime(String date, String engineerCode, List<OccupyInfoDetail> configs) {
// 计算一个工程师,一天的工作容量信息
// 省略实现细节
CapacityStats r = new CapacityStats();
......@@ -101,7 +105,7 @@ public class InitEngineerCapacityScheduler {
return new CapacityStats().setTotal(totalWorkTime).setUsed(totalLeaveTime).setRemain(totalWorkTime - totalLeaveTime);
}
private void initOneEngineerSlice(String date, String engineerCode, List<CapacityEngineerCalendarEntity> configs,
private void initOneEngineerSlice(String date, String engineerCode, List<OccupyInfoDetail> configs,
List<TimeSliceEntity> commonTimeSliceList) {
//查询时间片容量
List<CapacityEngineerSliceUsedEntity> engineerTimeSlice = EngineerSliceUsedCapacityService.findByWorkdayAndEngineerCode(date, engineerCode);
......@@ -110,6 +114,8 @@ public class InitEngineerCapacityScheduler {
return;
}
//TODO 日历需要添加
// 查询工程师正常的工作时间 并按小时切片:
EngineerBusinessEntity businessEntity = engineerBusinessDao.getByEngineerCode(engineerCode);
LocalDateTime workStartTime = DateUtils.localDateTimeFromStr(String.format("%s %s:00", date, businessEntity.getWorkOn()));
......@@ -134,8 +140,8 @@ public class InitEngineerCapacityScheduler {
//已用容量
long lengthOfLeave = 0L;
for (int i = 0; i < configs.size(); i++) {
CapacityEngineerCalendarEntity leave = configs.get(i);
LocalTime leaveStartTime = leave.getStartTime().toLocalTime();
OccupyInfoDetail leave = configs.get(i);
LocalTime leaveStartTime = leave.getBeginTime().toLocalTime();
LocalTime leaveEndTime = leave.getEndTime().toLocalTime();
boolean startIn = DateTimeUtil.isIn(leaveStartTime, sliceStartLocalTime, sliceEndLocalTime);
boolean endAfter = leaveEndTime.isAfter(sliceEndLocalTime);
......@@ -149,7 +155,7 @@ public class InitEngineerCapacityScheduler {
lengthOfLeave += DateTimeUtil.betweenTwoTime(leaveStartTime, sliceEndLocalTime, TimeUnit.MINUTES);
} else if (leaveStartTime.isBefore(sliceStartLocalTime) && leaveEndTime.isAfter(sliceEndLocalTime)) {
lengthOfLeave += DateTimeUtil.betweenTwoTime(sliceStartLocalTime, sliceEndLocalTime, TimeUnit.MINUTES);
} else if (leaveStartTime.isBefore(sliceStartLocalTime) && leaveEndTime.compareTo(sliceEndLocalTime) <=0) {
} else if (leaveStartTime.isBefore(sliceStartLocalTime) && leaveEndTime.compareTo(sliceEndLocalTime) <= 0) {
lengthOfLeave += DateTimeUtil.betweenTwoTime(sliceStartLocalTime, leaveEndTime, TimeUnit.MINUTES);
}
}
......@@ -172,8 +178,13 @@ public class InitEngineerCapacityScheduler {
//切片开始时间
LocalTime sliceStartHour = LocalTime.of(workStartTime.getHour(), 0);
//切片结束时间
LocalTime sliceEndHour = LocalTime.of(workEndTime.getHour(), 0);
LocalTime sliceEndHour;
int minuteOfHour = workEndTime.getMinute();
if (minuteOfHour > 0) {
sliceEndHour = LocalTime.of(workEndTime.getHour(), 0).plusHours(1);
} else {
sliceEndHour = LocalTime.of(workEndTime.getHour(), 0);
}
List<TimeSliceEntity> timeCorridor = commonTimeSliceList.stream()
.filter(slice -> {
......@@ -187,13 +198,9 @@ public class InitEngineerCapacityScheduler {
private void initOneEngineer(String date, String engineerCode) {
log.info("正在处理日期[{}] 技术员[{}]", date, engineerCode);
List<CapacityEngineerCalendarEntity> configs = capacityEngineerCalendarDao.findCalendarByWorkdayAndEngineerCode(date, engineerCode)
.stream().sorted(Comparator.comparing(CapacityEngineerCalendarEntity::getStartTime)).collect(Collectors.toList());
//事程日历添加工作队休息时间
List<OccupyInfoDetail> configs = engineerCalendarService.getEngineerWorkDayCalendar(engineerCode, LocalDate.parse(date, DateTimeUtil.DATE_FORMAT));
if (!configs.isEmpty() && !verifyCalendar(configs)) {
log.error("日期[{}]技术员[{}]请假配置检查失败,忽略退出", date, engineerCode);
return;
}
List<TimeSliceEntity> commonTimeSliceList = timeSliceDao.findByType("HOURS");
//初始化一个工程师时间切片容量
initOneEngineerSlice(date, engineerCode, configs, commonTimeSliceList);
......@@ -203,8 +210,7 @@ public class InitEngineerCapacityScheduler {
log.error("技术员容量信息记录已存在, 直接返回");
return;
}
String memo = configs.stream().map(CapacityEngineerCalendarEntity::getType).collect(Collectors.joining("/"));
log.info("日期[{}] 技术员[{}] 有日历记录需要特别处理 === {}", date, engineerCode, memo);
log.info("日期[{}] 技术员[{}] 有日历记录需要特别处理 ===", date, engineerCode);
CapacityStats stats = calculateWorkTime(date, engineerCode, configs);
log.info("日期[{}]技术员[{}],总容量[{}] 占用容量[{}] 剩余容量[{}]", date, engineerCode, stats.getTotal(), stats.getUsed(), stats.getRemain());
if (statEntity == null) {
......@@ -219,7 +225,6 @@ public class InitEngineerCapacityScheduler {
statEntity.setCapUsed((int) stats.getUsed());
statEntity.setCapLeft((int) stats.getRemain());
statEntity.setMaxDuration((int) stats.getRemain());
statEntity.setMemo(memo);
statEntity.setUpdateTime(LocalDateTime.now());
capacityEngineerStatDao.save(statEntity);
log.info("====== 处理完毕 ======");
......
package com.dituhui.pea.order.service;
import com.dituhui.pea.common.Result;
import com.dituhui.pea.order.dto.*;
import com.dituhui.pea.order.common.OccupyInfoDetail;
import com.dituhui.pea.order.dto.CalendarBatAddDTO;
import com.dituhui.pea.order.dto.CalendarBatDelDTO;
import com.dituhui.pea.order.dto.CalendarDeleteDTO;
import com.dituhui.pea.order.dto.CalendarDetailDTO;
import com.dituhui.pea.order.dto.CalendarQueryNumDTO;
import com.dituhui.pea.order.dto.CalendarUpdateDTO;
import com.dituhui.pea.order.dto.EngineerCalendarDTO;
import com.dituhui.pea.order.dto.param.EngineerCalendarResultDTO;
import java.time.LocalDate;
import java.util.List;
public interface EngineerCalendarService {
......@@ -26,10 +34,20 @@ public interface EngineerCalendarService {
/**
* 查询工程师日期范围内的日历事件安排, 返回的内容包括:(多条)开始时间,结束时间,事件代码,事件名称,事件说明
*
* @param engineerCode 工程师编码
* @param startDate 开始日期
* @param endDate 结束日期
* @return 日期范围内的日历, 包含开始时间,结束时间,事件代码,事件名称,事件说明
*/
EngineerCalendarResultDTO queryEngineerCalendar(String engineerCode, LocalDate startDate, LocalDate endDate);
/**
* 获取工程师在指定日期的非工作时间安排 包含工作队休息时间和日程表事件记录时间, 其中日程表内事件未与工作队时间判定交集, 实际计算非工作时间时需求并集
*
* @param engineerCode 工程师编号
* @param targetDate 目标日期
* @return 没有交集的事件日程时间段即已占用的时间段
*/
List<OccupyInfoDetail> getEngineerWorkDayCalendar(String engineerCode, LocalDate targetDate);
}
......@@ -145,8 +145,8 @@ public class CapacityQueryServiceImpl implements CapacityQueryService {
.mapToInt(SkillInfoEntity::getTakeTime)
.sum();
//查询单个工程师日期范围内的技能容量信息 后加和汇总
CopyOnWriteArrayList<TimeSliceEntity> halfDayTypeTimeSlice = new CopyOnWriteArrayList<>();//timeSliceDao.findByType("HALF_DAY"));
CopyOnWriteArrayList<TimeSliceEntity> allDayTypeTimeSlice = new CopyOnWriteArrayList<>();//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)));
......@@ -271,7 +271,14 @@ public class CapacityQueryServiceImpl implements CapacityQueryService {
// 同步到saas,返回blockId
List<String> blockIds = null;
if (StringUtils.isNotBlank(reqDTO.getLocation())) {
blockIds = saasUtils.queryBlocksByLocation(reqDTO.getLocation()).stream().map(SaasUtils.BlockInfo::getBlockId).collect(Collectors.toList());
FendanDTO fendanDTO = new FendanDTO();
fendanDTO.setXy(reqDTO.getLocation());
List<SaasUtils.BlockInfo> blockInfoList = saasUtils.queryBlocksByXy(fendanDTO);
if (blockInfoList.isEmpty()) {
return Result.failed(StatusCodeEnum.FENDAN_AREA_UNMATCHED);
}
//2:根据查询出区划匹配分站信息
blockIds = blockInfoList.stream().map(SaasUtils.BlockInfo::getBlockId).distinct().collect(Collectors.toList());
} else {
blockIds = saasUtils.queryBlocksByAddress(reqDTO.getAddress()).stream().map(SaasUtils.BlockInfo::getBlockId).collect(Collectors.toList());
}
......
......@@ -5,8 +5,10 @@ import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.IdUtil;
import com.dituhui.pea.common.BusinessException;
import com.dituhui.pea.common.Result;
import com.dituhui.pea.order.common.CapacityUtils;
import com.dituhui.pea.order.common.DateUtils;
import com.dituhui.pea.order.common.EngineerUtils;
import com.dituhui.pea.order.common.OccupyInfoDetail;
import com.dituhui.pea.order.common.jackson.DateTimeUtil;
import com.dituhui.pea.order.common.jackson.DateUtil;
import com.dituhui.pea.order.dao.CapacityEngineerCalendarDao;
......@@ -55,11 +57,14 @@ import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
......@@ -543,6 +548,54 @@ public class EngineerCalendarServiceImpl implements EngineerCalendarService {
}
}
/**
* 获取工程师在指定日期的非工作时间安排 包含工作队休息时间和日程表事件记录时间, 其中日程表内事件未与工作队时间判定交集, 实际计算非工作时间时需求并集
*
* @param engineerCode 工程师编号
* @param targetDate 目标日期
* @return 没有交集的事件日程时间段即已占用的时间段
*/
@Override
public List<OccupyInfoDetail> getEngineerWorkDayCalendar(String engineerCode, LocalDate targetDate) {
//日程表事件记录时间
List<CapacityEngineerCalendarEntity> configs = capacityEngineerCalendarDao.findCalendarByWorkdayAndEngineerCode(DateTimeUtil.formatDate(targetDate), engineerCode);
// 这里没有重复时间段
List<OccupyInfoDetail> calendarInfoList = Optional.ofNullable(configs).orElse(Collections.emptyList())
.stream()
.map(leave -> {
LocalTime leaveStartTime = leave.getStartTime().toLocalTime();
LocalTime leaveEndTime = leave.getEndTime().toLocalTime();
return new OccupyInfoDetail(LocalDateTime.of(targetDate, leaveStartTime), LocalDateTime.of(targetDate, leaveEndTime));
}).collect(Collectors.toList());
//获取工作队休息时间, 判定目标时间是否在工作队休息日中
Set<OrgTeamEntity> teams = orgTeamDao.selectTeamByEngineerCode(engineerCode);
if (!CollectionUtils.isEmpty(teams)) {
List<String> teamCommonWorkdaysOfWeek = teams.stream()
.map(team -> Arrays.asList(team.getWorkdays().split(",")))
.flatMap(Collection::stream)
.distinct()
.collect(Collectors.toList());
List<String> allWorkDayOfWeek = List.of("1", "2", "3", "4", "5", "6", "7");
// 求多个工作队的公共空闲时间
final int dayOfWeek = targetDate.getDayOfWeek().getValue();
List<String> commonLeisureDayOfWeek = allWorkDayOfWeek.stream().filter(day -> teamCommonWorkdaysOfWeek.stream().noneMatch(tDay -> Objects.equals(day, tDay)))
.collect(Collectors.toList());
if (commonLeisureDayOfWeek.contains(String.valueOf(dayOfWeek))) {
LocalDateTime startTime = LocalDateTime.of(targetDate, LocalTime.of(8, 0));
LocalDateTime endTime = LocalDateTime.of(targetDate, LocalTime.of(23, 59, 59));
calendarInfoList.add(new OccupyInfoDetail(startTime, endTime));
}
//求所有事程时间与工作队休息时间的并集
calendarInfoList = CapacityUtils.calculateUnion(calendarInfoList);
}
return calendarInfoList;
}
private EngineerCalendarDTO.Calendar getEmptyCalendar(String teamId, String date) {
// 初始化一天的日历
OrgTeamEntity e = orgTeamDao.getByTeamId(teamId);
......
......@@ -79,6 +79,21 @@ public class CapacityUtilsTest {
occupyInfoDetails1 = CapacityUtils.calculateUnion(occupyInfoDetails);
Assertions.assertEquals(2, occupyInfoDetails1.size());
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, 0)
), LocalDateTime.of(LocalDate.now(), LocalTime.of(12, 0))));
occupyInfoDetails1 = CapacityUtils.calculateUnion(occupyInfoDetails);
Assertions.assertEquals(1, occupyInfoDetails1.size());
}
@Test
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!