Commit 949a10fe by 刘鑫

feat(单个工程师容量): 新增单个工程师单日期时间切片容量存储

1 parent f26f6af3
......@@ -4,11 +4,14 @@ import lombok.experimental.UtilityClass;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
......@@ -29,7 +32,7 @@ public class DateTimeUtil {
/**
* 时间格式 HH:mm:ss
*/
public static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern(DateUtil.PATTERN_TIME);
public static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern(DateUtil.PATTERN_TIME);
/**
* 日期时间格式化
......@@ -161,4 +164,28 @@ public class DateTimeUtil {
return result;
}
public static void main(String[] args) {
long l = betweenTwoTime( LocalTime.of(8, 0),
LocalTime.of(9, 0), TimeUnit.MINUTES);
System.out.printf(""+l);
}
/**
* 当前时间是否在时间指定范围内, 包含起始时间<br>
*
* @param time 被检查的时间
* @param beginTime 起始时间
* @param endTime 结束时间
* @return 是否在范围内
* @since 3.0.8
*/
public static boolean isIn(LocalTime time, LocalTime beginTime, LocalTime endTime) {
if ((Objects.equals(beginTime, time) || beginTime.isBefore(time))
&& (endTime.isAfter(time) || Objects.equals(endTime, time))) {
return true;
}
return false;
}
}
package com.dituhui.pea.order.dao;
import com.dituhui.pea.order.entity.CapacityEngineerSliceUsedEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface EngineerSliceUsedCapacityDao extends JpaRepository<CapacityEngineerSliceUsedEntity, Long> {
List<CapacityEngineerSliceUsedEntity> findByWorkdayAndEngineerCode(String workDay, String engineerCode);
}
package com.dituhui.pea.order.dao;
import com.dituhui.pea.order.entity.TimeSliceEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface TimeSliceDao extends JpaRepository<TimeSliceEntity, Long> {
List<TimeSliceEntity> findByType(String type);
}
package com.dituhui.pea.order.entity;
import lombok.Getter;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import java.time.LocalDateTime;
import java.util.Objects;
@Getter
@Entity
@Table(name = "capacity_engineer_slice_used")
public class CapacityEngineerSliceUsedEntity {
@Id
@Column(name = "id")
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid")
private String id;
@OneToOne(targetEntity = TimeSliceEntity.class,
cascade = {CascadeType.DETACH},
fetch = FetchType.EAGER)
@JoinColumn(name = "timme_slice", referencedColumnName = "id")
private TimeSliceEntity timmeSlice;
@Basic
@Column(name = "workday")
private String workday;
@Basic
@Column(name = "engineer_code")
private String engineerCode;
@Basic
@Column(name = "cap_total")
private Long capTotal;
@Basic
@Column(name = "cap_used")
private Long capUsed;
@Basic
@Column(name = "cap_used_travel")
private Long capUsedTravel;
@Basic
@Column(name = "cap_left")
private Long capLeft;
@Basic
@Column(name = "order_count")
private Long orderCount;
@Basic
@Column(name = "max_duration")
private Long maxDuration;
@Basic
@Column(name = "max_duration_type")
private String maxDurationType;
@Basic
@Column(name = "memo")
private String memo;
@Basic
@Column(name = "create_time")
private LocalDateTime createTime;
@Basic
@Column(name = "update_time")
private LocalDateTime updateTime;
public void setId(String id) {
this.id = id;
}
public void setTimmeSlice(TimeSliceEntity timmeSlice) {
this.timmeSlice = timmeSlice;
}
public void setWorkday(String workday) {
this.workday = workday;
}
public void setEngineerCode(String engineerCode) {
this.engineerCode = engineerCode;
}
public void setCapTotal(Long capTotal) {
this.capTotal = capTotal;
}
public void setCapUsed(Long capUsed) {
this.capUsed = capUsed;
}
public void setCapUsedTravel(Long capUsedTravel) {
this.capUsedTravel = capUsedTravel;
}
public void setCapLeft(Long capLeft) {
this.capLeft = capLeft;
}
public void setOrderCount(Long orderCount) {
this.orderCount = orderCount;
}
public void setMaxDuration(Long maxDuration) {
this.maxDuration = maxDuration;
}
public void setMaxDurationType(String maxDurationType) {
this.maxDurationType = maxDurationType;
}
public void setMemo(String memo) {
this.memo = memo;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CapacityEngineerSliceUsedEntity that = (CapacityEngineerSliceUsedEntity) o;
return id == that.id && timmeSlice == that.timmeSlice && Objects.equals(workday, that.workday) && Objects.equals(engineerCode, that.engineerCode) && Objects.equals(capTotal, that.capTotal) && Objects.equals(capUsed, that.capUsed) && Objects.equals(capUsedTravel, that.capUsedTravel) && Objects.equals(capLeft, that.capLeft) && Objects.equals(orderCount, that.orderCount) && Objects.equals(maxDuration, that.maxDuration) && Objects.equals(maxDurationType, that.maxDurationType) && Objects.equals(memo, that.memo) && Objects.equals(createTime, that.createTime) && Objects.equals(updateTime, that.updateTime);
}
@Override
public int hashCode() {
return Objects.hash(id, timmeSlice, workday, engineerCode, capTotal, capUsed, capUsedTravel, capLeft, orderCount, maxDuration, maxDurationType, memo, createTime, updateTime);
}
}
package com.dituhui.pea.order.entity;
import lombok.Getter;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.time.LocalDateTime;
import java.util.Objects;
@Getter
@Entity
@Table(name = "time_slice")
public class TimeSliceEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
@Column(name = "id")
private Long id;
@Basic
@Column(name = "type")
private String type;
@Basic
@Column(name = "name")
private String name;
@Basic
@Column(name = "start")
private String start;
@Basic
@Column(name = "end")
private String end;
@Basic
@Column(name = "create_time")
private LocalDateTime createTime;
@Basic
@Column(name = "update_time")
private LocalDateTime updateTime;
public void setId(Long id) {
this.id = id;
}
public void setType(String type) {
this.type = type;
}
public void setName(String name) {
this.name = name;
}
public void setStart(String start) {
this.start = start;
}
public void setEnd(String end) {
this.end = end;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TimeSliceEntity that = (TimeSliceEntity) o;
return id == that.id && Objects.equals(type, that.type) && Objects.equals(name, that.name) && Objects.equals(start, that.start) && Objects.equals(end, that.end) && Objects.equals(createTime, that.createTime) && Objects.equals(updateTime, that.updateTime);
}
@Override
public int hashCode() {
return Objects.hash(id, type, name, start, end, createTime, updateTime);
}
}
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.jackson.DateTimeUtil;
import com.dituhui.pea.order.common.jackson.DateUtil;
import com.dituhui.pea.order.dao.CapacityEngineerCalendarDao;
import com.dituhui.pea.order.dao.CapacityEngineerStatDao;
import com.dituhui.pea.order.dao.EngineerBusinessDao;
import com.dituhui.pea.order.dao.EngineerInfoDao;
import com.dituhui.pea.order.dao.EngineerSliceUsedCapacityDao;
import com.dituhui.pea.order.dao.TimeSliceDao;
import com.dituhui.pea.order.entity.CapacityEngineerCalendarEntity;
import com.dituhui.pea.order.entity.CapacityEngineerSliceUsedEntity;
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 lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
......@@ -20,8 +27,11 @@ import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Slf4j
......@@ -45,6 +55,10 @@ public class InitEngineerCapacityScheduler {
private CapacityEngineerStatDao capacityEngineerStatDao;
@Autowired
private EngineerInfoDao engineerInfoDao;
@Autowired
private TimeSliceDao timeSliceDao;
@Autowired
private EngineerSliceUsedCapacityDao engineerSliceUsedCapacityDao;
private boolean verifyCalendar(List<CapacityEngineerCalendarEntity> configs) {
// 检查多条请假配置是否有交叉行为; configs已经根据startTime排序
......@@ -84,19 +98,99 @@ public class InitEngineerCapacityScheduler {
return new CapacityStats().setTotal(totalWorkTime).setUsed(totalLeaveTime).setRemain(totalWorkTime - totalLeaveTime);
}
private void initOneEngineer(String date, String engineerCode) {
log.info("正在处理日期[{}] 技术员[{}]", date, engineerCode);
// 初始化一个工程师、一天的容量
CapacityEngineerStatEntity statEntity = capacityEngineerStatDao.getByWorkdayAndEngineerCode(date, engineerCode);
if (statEntity != null && !rewriteForce) {
log.error("技术员容量信息记录已存在, 直接返回");
private void initOneEngineerSlice(String date, String engineerCode, List<CapacityEngineerCalendarEntity> configs,
List<TimeSliceEntity> commonTimeSliceList) {
//查询时间片容量
List<CapacityEngineerSliceUsedEntity> engineerTimeSlice = engineerSliceUsedCapacityDao.findByWorkdayAndEngineerCode(date, engineerCode);
if (!CollectionUtil.isEmpty(engineerTimeSlice) && !rewriteForce) {
log.warn("工程师:{}存在日期:{}时间切片记录, 无需初始化", engineerCode, date);
return;
}
// 查询工程师正常的工作时间 并按小时切片:
EngineerBusinessEntity businessEntity = engineerBusinessDao.getByEngineerCode(engineerCode);
LocalDateTime workStartTime = DateUtils.localDateTimeFromStr(String.format("%s %s:00", date, businessEntity.getWorkOn()));
LocalDateTime workEndTime = DateUtils.localDateTimeFromStr(String.format("%s %s:00", date, businessEntity.getWorkOff()));
List<TimeSliceEntity> timeCorridor = getTimeSliceEntities(workStartTime, workEndTime, commonTimeSliceList);
ArrayList<CapacityEngineerSliceUsedEntity> resultList = new ArrayList<>(timeCorridor.size());
for (TimeSliceEntity timeSlice : timeCorridor) {
CapacityEngineerSliceUsedEntity r = new CapacityEngineerSliceUsedEntity();
r.setTimmeSlice(timeSlice);
r.setEngineerCode(engineerCode);
r.setWorkday(date);
LocalTime sliceStartLocalTime = LocalTime.parse(timeSlice.getStart(), DateUtil.TIME_FORMATTER);
LocalTime sliceEndLocalTime = LocalTime.parse(timeSlice.getEnd(), DateUtil.TIME_FORMATTER);
r.setCapTotal(DateTimeUtil.betweenTwoTime(sliceStartLocalTime, sliceEndLocalTime, TimeUnit.MINUTES));
//校验是否有在当前时间段的请假时间
//已用容量
long lengthOfLeave = 0L;
for (int i = 0; i < configs.size(); i++) {
CapacityEngineerCalendarEntity leave = configs.get(i);
LocalTime leaveStartTime = leave.getStartTime().toLocalTime();
LocalTime leaveEndTime = leave.getEndTime().toLocalTime();
boolean startIn = DateTimeUtil.isIn(leaveStartTime, sliceStartLocalTime, sliceEndLocalTime);
boolean endAfter = leaveEndTime.isAfter(sliceEndLocalTime);
//8:00- 8:30 8:00 - 10:30
//请假时间仅落在当前时间段内, 当前时间段请假时长为 请假结束时间- 请假开始时间
if (startIn && DateTimeUtil.isIn(leaveEndTime, sliceStartLocalTime, sliceEndLocalTime)) {
lengthOfLeave += DateTimeUtil.betweenTwoTime(leaveStartTime, leaveEndTime, TimeUnit.MINUTES);
} else if (startIn && endAfter) {
//落在当前时间段和下一个时间段
lengthOfLeave += DateTimeUtil.betweenTwoTime(leaveStartTime, sliceEndLocalTime, TimeUnit.MINUTES);
} else if (leaveStartTime.isBefore(sliceStartLocalTime) && leaveEndTime.isAfter(sliceEndLocalTime)) {
lengthOfLeave += DateTimeUtil.betweenTwoTime(sliceStartLocalTime, sliceEndLocalTime, TimeUnit.MINUTES);
}
}
// 剩余可约容量
long leftUseTime = 60 - lengthOfLeave;
r.setCapLeft(leftUseTime);
r.setCapUsed(lengthOfLeave);
r.setCreateTime(LocalDateTime.now());
r.setUpdateTime(LocalDateTime.now());
resultList.add(r);
}
engineerSliceUsedCapacityDao.saveAll(resultList);
}
private List<TimeSliceEntity> getTimeSliceEntities(LocalDateTime workStartTime, LocalDateTime workEndTime,
List<TimeSliceEntity> commonTimeSliceList) {
//切片开始时间
LocalTime sliceStartHour = LocalTime.of(workStartTime.getHour(), 0);
//切片结束时间
LocalTime sliceEndHour = LocalTime.of(workEndTime.getHour(), 0);
List<TimeSliceEntity> timeCorridor = commonTimeSliceList.stream()
.filter(slice -> {
LocalTime startLocalTime = LocalTime.parse(slice.getStart(), DateUtil.TIME_FORMATTER);
LocalTime endLocalTime = LocalTime.parse(slice.getEnd(), DateUtil.TIME_FORMATTER);
return (startLocalTime.isAfter(sliceStartHour) && endLocalTime.isBefore(sliceEndHour)) ||
(startLocalTime.equals(sliceStartHour) || endLocalTime.equals(sliceEndHour));
}).sorted(Comparator.comparing(TimeSliceEntity::getId)).collect(Collectors.toList());
return timeCorridor;
}
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());
if (!configs.isEmpty() && !verifyCalendar(configs)) {
log.error("配置检查失败,忽略退出");
log.error("日期[{}]技术员[{}]请假配置检查失败,忽略退出", date, engineerCode);
return;
}
List<TimeSliceEntity> commonTimeSliceList = timeSliceDao.findByType("HOURS");
//初始化一个工程师时间切片容量
initOneEngineerSlice(date, engineerCode, configs, commonTimeSliceList);
// 初始化一个工程师、一天的容量
CapacityEngineerStatEntity statEntity = capacityEngineerStatDao.getByWorkdayAndEngineerCode(date, engineerCode);
if (statEntity != null && !rewriteForce) {
log.error("技术员容量信息记录已存在, 直接返回");
return;
}
String memo = configs.stream().map(CapacityEngineerCalendarEntity::getType).collect(Collectors.joining("/"));
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!