Skip to content
Toggle navigation
Projects
Groups
Snippets
Help
yangxiujun
/
paidan_demo
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit e5ccf54d
authored
Nov 01, 2023
by
刘鑫
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(单时间片容量): 单时间片容量查询
1 parent
5a1074da
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
149 additions
and
67 deletions
project-order/src/main/java/com/dituhui/pea/order/common/CapacityUtils.java
project-order/src/main/java/com/dituhui/pea/order/common/OccupyInfo.java
project-order/src/main/java/com/dituhui/pea/order/common/jackson/DateUtil.java
project-order/src/main/java/com/dituhui/pea/order/scheduler/CalcEngineerCapacityScheduler.java
project-order/src/main/java/com/dituhui/pea/order/service/impl/CapacityQueryServiceImpl.java
project-order/src/main/java/com/dituhui/pea/order/common/CapacityUtils.java
View file @
e5ccf54
...
...
@@ -4,8 +4,10 @@ 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
;
import
com.dituhui.pea.order.entity.SkillInfoEntity
;
import
com.dituhui.pea.order.entity.TimeSliceEntity
;
import
lombok.extern.slf4j.Slf4j
;
...
...
@@ -13,17 +15,23 @@ import org.apache.commons.collections.CollectionUtils;
import
org.springframework.beans.factory.annotation.Autowired
;
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.Collection
;
import
java.util.Collections
;
import
java.util.Comparator
;
import
java.util.List
;
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
;
import
java.util.stream.Collectors
;
import
java.util.stream.Stream
;
@Component
@Slf4j
...
...
@@ -70,6 +78,48 @@ public class CapacityUtils {
return
layers
;
}
//根据capacity_engineer_calendar和order_info,来确定当天剩下的最大连续时间区块 和已使用容量;
public
static
List
<
OccupyInfo
>
caculate
(
final
LocalDateTime
startTime
,
final
LocalDateTime
endTime
,
List
<
OrderInfoEntity
>
orders
,
List
<
CapacityEngineerCalendarEntity
>
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
))
.
collect
(
Collectors
.
toList
());
// 工单已使用的时间片
List
<
OccupyInfo
>
order
=
Optional
.
ofNullable
(
orders
).
orElse
(
Collections
.
emptyList
())
.
stream
()
.
filter
(
t
->
DateUtil
.
checkTimesHasOverlap
(
t
.
getPlanStartTime
(),
t
.
getPlanEndTime
(),
startTime
,
endTime
))
.
map
(
e
->
DateUtil
.
timesOverlap
(
e
.
getPlanStartTime
(),
e
.
getPlanEndTime
(),
startTime
,
endTime
))
.
collect
(
Collectors
.
toList
());
//存放所有的已用时间段信息
return
Stream
.
of
(
calendar
,
order
).
flatMap
(
Collection:
:
stream
)
.
sorted
(
Comparator
.
comparing
(
OccupyInfo:
:
getBeginTime
)).
collect
(
Collectors
.
toList
());
}
public
static
Long
getMaxRemainBlock
(
LocalDateTime
startTime
,
LocalDateTime
endTime
,
List
<
OccupyInfo
>
occupyInfos
)
{
if
(
org
.
springframework
.
util
.
CollectionUtils
.
isEmpty
(
occupyInfos
))
{
return
Duration
.
between
(
startTime
,
endTime
).
toMinutes
();
}
//空闲时间
List
<
Long
>
idlePeriods
=
new
ArrayList
<>();
LocalDateTime
preLast
=
startTime
;
for
(
OccupyInfo
o
:
occupyInfos
)
{
if
(
o
.
getBeginTime
().
isAfter
(
preLast
))
{
idlePeriods
.
add
(
Duration
.
between
(
startTime
,
o
.
getBeginTime
()).
toMinutes
());
}
preLast
=
o
.
getEndTime
();
}
if
(
preLast
.
isBefore
(
endTime
))
{
idlePeriods
.
add
(
Duration
.
between
(
preLast
,
endTime
).
toMinutes
());
}
return
org
.
springframework
.
util
.
CollectionUtils
.
isEmpty
(
idlePeriods
)
?
Duration
.
between
(
startTime
,
endTime
).
toMinutes
()
:
Collections
.
max
(
idlePeriods
);
}
public
static
List
<
CapacityQueryDTO
.
Segment
>
getEngineerTypeDay
(
List
<
TimeSliceEntity
>
typeTimeSlice
,
List
<
CapacityEngineerSliceUsedEntity
>
engineerTimeSlice
,
...
...
project-order/src/main/java/com/dituhui/pea/order/common/OccupyInfo.java
0 → 100644
View file @
e5ccf54
package
com
.
dituhui
.
pea
.
order
.
common
;
import
lombok.AllArgsConstructor
;
import
lombok.Data
;
import
lombok.NoArgsConstructor
;
import
lombok.experimental.Accessors
;
import
java.time.LocalDateTime
;
@Data
@Accessors
(
chain
=
true
)
@AllArgsConstructor
@NoArgsConstructor
public
class
OccupyInfo
{
private
LocalDateTime
beginTime
;
private
LocalDateTime
endTime
;
}
project-order/src/main/java/com/dituhui/pea/order/common/jackson/DateUtil.java
View file @
e5ccf54
package
com
.
dituhui
.
pea
.
order
.
common
.
jackson
;
import
com.dituhui.pea.order.common.DateSplit
;
import
com.dituhui.pea.order.common.OccupyInfo
;
import
lombok.experimental.UtilityClass
;
import
org.springframework.util.Assert
;
...
...
@@ -9,6 +10,7 @@ import java.time.Duration;
import
java.time.Instant
;
import
java.time.LocalDate
;
import
java.time.LocalDateTime
;
import
java.time.LocalTime
;
import
java.time.Period
;
import
java.time.ZoneId
;
import
java.time.ZonedDateTime
;
...
...
@@ -470,6 +472,7 @@ public class DateUtil {
return
Duration
.
between
(
startDate
.
toInstant
(),
endDate
.
toInstant
());
}
/**
* 判断两个时间范围是否有交集
*
...
...
@@ -477,16 +480,25 @@ public class DateUtil {
* @param dynaEndTime 比较时间段结束时间
* @param fixedStartTime 参考时间段开始时间
* @param fixedEndTime 参考时间段结束时间
* @return
* @return
求交集时间片时 返回交集时间片, 没有对应情况则返回null
*/
public
static
boolean
checkTimesHasOverlap
(
Date
dynaStartTime
,
Date
dynaEndTime
,
Date
fixedStartTime
,
Date
fixedEndTime
)
{
if
(
dynaStartTime
.
getTime
()
<=
fixedStartTime
.
getTime
()
&&
dynaEndTime
.
getTime
()
>
fixedStartTime
.
getTime
())
{
return
true
;
}
else
if
(
dynaStartTime
.
getTime
()
>=
fixedStartTime
.
getTime
()
&&
dynaStartTime
.
getTime
()
<
fixedEndTime
.
getTime
())
{
return
true
;
}
else
{
return
false
;
public
static
OccupyInfo
timesOverlap
(
LocalDateTime
dynaStartTime
,
LocalDateTime
dynaEndTime
,
LocalDateTime
fixedStartTime
,
LocalDateTime
fixedEndTime
)
{
return
intersection
(
dynaStartTime
,
dynaEndTime
,
fixedStartTime
,
fixedEndTime
);
}
// 计算两个时间片的交集
public
OccupyInfo
intersection
(
LocalDateTime
dynaStartTime
,
LocalDateTime
dynaEndTime
,
LocalDateTime
fixedStartTime
,
LocalDateTime
fixedEndTime
)
{
// 如果两个时间片没有交集,返回null
if
(!
checkTimesHasOverlap
(
dynaStartTime
,
dynaEndTime
,
fixedStartTime
,
fixedEndTime
))
{
return
null
;
}
// 否则,返回一个新的时间片,其开始时间是两个时间片中较晚的开始时间,其结束时间是两个时间片中较早的结束时间
return
new
OccupyInfo
(
dynaStartTime
.
isAfter
(
fixedStartTime
)
?
dynaStartTime
:
fixedStartTime
,
dynaEndTime
.
isBefore
(
fixedEndTime
)
?
dynaEndTime
:
fixedEndTime
);
}
/**
...
...
@@ -496,17 +508,42 @@ public class DateUtil {
* @param dynaEndTime 比较时间段结束时间
* @param fixedStartTime 参考时间段开始时间
* @param fixedEndTime 参考时间段结束时间
* @return
* @return 是否有交集
* @apiNote 要计算两个时间段的交集,我们可以使用以下公式:
* max(fixedStartTime, dynaStartTime) < min(fixedEndTime, dynaEndTime)
* 如果这个条件成立,说明两个时间段有交集,否则没有交集。
*/
public
static
boolean
checkTimesHasOverlap
(
LocalDateTime
dynaStartTime
,
LocalDateTime
dynaEndTime
,
LocalDateTime
fixedStartTime
,
LocalDateTime
fixedEndTime
)
{
if
(
dynaStartTime
.
compareTo
(
fixedStartTime
)
<=
0
&&
dynaEndTime
.
compareTo
(
fixedStartTime
)
>
0
)
{
return
true
;
}
else
if
(
dynaStartTime
.
compareTo
(
fixedStartTime
)
>=
0
&&
dynaStartTime
.
compareTo
(
fixedEndTime
)
<
0
)
{
return
true
;
}
else
{
return
false
;
}
// 取两个开始时间中较大的一个
LocalDateTime
maxStartTime
=
dynaStartTime
.
compareTo
(
fixedStartTime
)
>
0
?
dynaStartTime
:
fixedStartTime
;
// 取两个结束时间中较小的一个
LocalDateTime
minEndTime
=
dynaEndTime
.
compareTo
(
fixedEndTime
)
<
0
?
dynaEndTime
:
fixedEndTime
;
return
maxStartTime
.
isBefore
(
minEndTime
);
}
/**
* 判断两个时间范围是否有交集
*
* @param dynaStartTime 比较时间段开始时间
* @param dynaEndTime 比较时间段结束时间
* @param fixedStartTime 参考时间段开始时间
* @param fixedEndTime 参考时间段结束时间
* @return 是否有交集
* @apiNote 要计算两个时间段的交集,我们可以使用以下公式:
* max(fixedStartTime, dynaStartTime) < min(fixedEndTime, dynaEndTime)
* 如果这个条件成立,说明两个时间段有交集,否则没有交集。
*/
public
static
boolean
checkTimesHasOverlap
(
LocalTime
dynaStartTime
,
LocalTime
dynaEndTime
,
LocalTime
fixedStartTime
,
LocalTime
fixedEndTime
)
{
// 取两个开始时间中较大的一个
LocalTime
maxStartTime
=
dynaStartTime
.
compareTo
(
fixedStartTime
)
>
0
?
dynaStartTime
:
fixedStartTime
;
// 取两个结束时间中较小的一个
LocalTime
minEndTime
=
dynaEndTime
.
compareTo
(
fixedEndTime
)
<
0
?
dynaEndTime
:
fixedEndTime
;
return
maxStartTime
.
isBefore
(
minEndTime
);
}
...
...
project-order/src/main/java/com/dituhui/pea/order/scheduler/CalcEngineerCapacityScheduler.java
View file @
e5ccf54
package
com
.
dituhui
.
pea
.
order
.
scheduler
;
import
com.dituhui.pea.order.common.CapacityUtils
;
import
com.dituhui.pea.order.common.DateUtils
;
import
com.dituhui.pea.order.common.OccupyInfo
;
import
com.dituhui.pea.order.common.jackson.DateTimeUtil
;
import
com.dituhui.pea.order.common.jackson.DateUtil
;
import
com.dituhui.pea.order.dao.CapacityEngineerCalendarDao
;
...
...
@@ -17,8 +19,6 @@ 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
lombok.Data
;
import
lombok.experimental.Accessors
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Value
;
...
...
@@ -34,9 +34,8 @@ import java.util.ArrayList;
import
java.util.Collections
;
import
java.util.Comparator
;
import
java.util.List
;
import
java.util.O
bjects
;
import
java.util.O
ptional
;
import
java.util.Set
;
import
java.util.concurrent.TimeUnit
;
import
java.util.stream.Collectors
;
@Slf4j
...
...
@@ -95,7 +94,14 @@ public class CalcEngineerCapacityScheduler {
// 根据capacity_engineer_calendar和order_info,来确定当天剩下的最大连续时间区块;
EngineerBusinessEntity
businessEntity
=
engineerBusinessDao
.
getByEngineerCode
(
engineerCode
);
// 就算单小时时间段工程师技能已用容量存储至时间切片工程师时间表内
extracted
(
date
,
engineerCode
,
orders
,
ss
);
//添加工程师日历
List
<
CapacityEngineerCalendarEntity
>
configs
=
capacityEngineerCalendarDao
.
findCalendarByWorkdayAndEngineerCode
(
date
,
engineerCode
);
//剔除重排和取消单
orders
=
Optional
.
ofNullable
(
orders
).
orElse
(
Collections
.
emptyList
())
.
stream
()
.
filter
(
e
->
!
ss
.
contains
(
e
.
getOrderStatus
()))
.
collect
(
Collectors
.
toList
());
initOneEngineerTimeSlot
(
date
,
engineerCode
,
configs
,
orders
);
int
used
=
orders
.
stream
().
map
(
e
->
{
...
...
@@ -105,9 +111,7 @@ public class CalcEngineerCapacityScheduler {
return
e
.
getTakeTime
();
}
}).
mapToInt
(
Integer:
:
intValue
).
sum
();
long
cnt
=
orders
.
stream
()
.
filter
(
e
->
!
ss
.
contains
(
e
.
getOrderStatus
()))
.
count
();
long
cnt
=
orders
.
size
();
long
max
=
getMaxRemainBlock
(
date
,
engineerCode
,
orders
,
businessEntity
);
log
.
info
(
"正在处理: 日期[{}]技术员[{}]容量相关信息 ==> used:{}, orderCnt:{}, maxDuration:{}"
,
date
,
engineerCode
,
used
,
cnt
,
max
);
statEntity
.
setOrderCount
((
int
)
cnt
);
...
...
@@ -118,56 +122,36 @@ public class CalcEngineerCapacityScheduler {
capacityEngineerStatDao
.
save
(
statEntity
);
}
private
void
extracted
(
String
date
,
String
engineerCode
,
List
<
OrderInfoEntity
>
orders
,
Set
<
String
>
ss
)
{
//添加工程师日历参数
private
void
initOneEngineerTimeSlot
(
String
date
,
String
engineerCode
,
List
<
CapacityEngineerCalendarEntity
>
configs
,
List
<
OrderInfoEntity
>
orders
)
{
final
LocalDate
localDate
=
LocalDate
.
parse
(
date
,
DateTimeUtil
.
DATE_FORMAT
);
//查询时间片容量
List
<
CapacityEngineerSliceUsedEntity
>
engineerTimeSlice
=
engineerSliceUsedCapacityDao
.
findByWorkdayAndEngineerCode
(
date
,
engineerCode
);
for
(
CapacityEngineerSliceUsedEntity
sliceCap
:
engineerTimeSlice
)
{
final
TimeSliceEntity
timeSlice
=
sliceCap
.
getTimmeSlice
();
LocalTime
sliceStartLocalTime
=
LocalTime
.
parse
(
timeSlice
.
getStart
(),
DateUtil
.
TIME_FORMATTER
);
LocalTime
sliceEndLocalTime
=
LocalTime
.
parse
(
timeSlice
.
getEnd
(),
DateUtil
.
TIME_FORMATTER
);
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
();
boolean
startIn
=
DateTimeUtil
.
isIn
(
planStartTime
,
sliceStartLocalTime
,
sliceEndLocalTime
);
boolean
endAfter
=
planEndTime
.
isAfter
(
sliceEndLocalTime
);
//8:00- 8:30 8:00 - 10:30
boolean
contains
=
ss
.
contains
(
order
.
getOrderStatus
());
//请假时间仅落在当前时间段内, 当前时间段请假时长为 请假结束时间- 请假开始时间
if
(
startIn
&&
DateTimeUtil
.
isIn
(
planEndTime
,
sliceStartLocalTime
,
sliceEndLocalTime
))
{
long
useTime
=
DateTimeUtil
.
betweenTwoTime
(
planStartTime
,
planEndTime
,
TimeUnit
.
MINUTES
);
totalUseTime
=
contains
?
totalUseTime
+
useTime
:
totalUseTime
-
useTime
;
}
else
if
(
startIn
&&
endAfter
)
{
//落在当前时间段和下一个时间段
long
useTime
=
DateTimeUtil
.
betweenTwoTime
(
planStartTime
,
sliceEndLocalTime
,
TimeUnit
.
MINUTES
);
totalUseTime
=
contains
?
totalUseTime
+
useTime
:
totalUseTime
-
useTime
;
}
else
if
(
planStartTime
.
isBefore
(
sliceStartLocalTime
)
&&
endAfter
)
{
long
usedTime
=
DateTimeUtil
.
betweenTwoTime
(
sliceStartLocalTime
,
sliceEndLocalTime
,
TimeUnit
.
MINUTES
);
totalUseTime
=
contains
?
totalUseTime
+
usedTime
:
totalUseTime
-
usedTime
;
}
LocalDateTime
startTime
=
LocalDateTime
.
of
(
localDate
,
sliceStartLocalTime
);
LocalDateTime
endTime
=
LocalDateTime
.
of
(
localDate
,
sliceEndLocalTime
);
List
<
OccupyInfo
>
occupyInfo
=
CapacityUtils
.
caculate
(
startTime
,
endTime
,
orders
,
configs
);
//已用容量
long
totalUseTime
=
occupyInfo
.
stream
().
mapToLong
(
t
->
Duration
.
between
(
t
.
getEndTime
(),
t
.
getBeginTime
()).
toMinutes
()).
sum
();
//最大连续时长
Long
maxRemainBlock
=
CapacityUtils
.
getMaxRemainBlock
(
startTime
,
endTime
,
occupyInfo
);
}
if
(
totalUseTime
<
0
)
{
totalUseTime
=
0
;
}
sliceCap
.
setCapLeft
(
sliceCap
.
getCapTotal
()
-
totalUseTime
);
sliceCap
.
setMaxDuration
(
sliceCap
.
getCapTotal
()
-
totalUseTime
);
sliceCap
.
setCapUsed
(
totalUseTime
);
sliceCap
.
setOrderCount
((
long
)
orders
.
size
());
sliceCap
.
setMaxDuration
(
maxRemainBlock
);
sliceCap
.
setUpdateTime
(
LocalDateTime
.
now
(
ZoneId
.
of
(
"+8"
)));
}
engineerSliceUsedCapacityDao
.
saveAll
(
engineerTimeSlice
);
}
private
long
getMaxRemainBlock
(
String
date
,
String
engineerCode
,
List
<
OrderInfoEntity
>
orders
,
EngineerBusinessEntity
businessEntity
)
{
// 根据capacity_engineer_calendar和order_info,来确定当天剩下的最大连续时间区块;
LocalDateTime
startTime
=
DateUtils
.
localDateTimeFromStr
(
String
.
format
(
"%s %s:00"
,
date
,
businessEntity
.
getWorkOn
()));
...
...
@@ -208,10 +192,4 @@ public class CalcEngineerCapacityScheduler {
}
}
@Data
@Accessors
(
chain
=
true
)
private
static
class
OccupyInfo
{
private
LocalDateTime
beginTime
;
private
LocalDateTime
endTime
;
}
}
project-order/src/main/java/com/dituhui/pea/order/service/impl/CapacityQueryServiceImpl.java
View file @
e5ccf54
...
...
@@ -154,7 +154,7 @@ public class CapacityQueryServiceImpl implements CapacityQueryService {
final
int
corePoolSize
=
Runtime
.
getRuntime
().
availableProcessors
();
ThreadPoolExecutor
poolExecutor
=
new
ThreadPoolExecutor
(
corePoolSize
,
corePoolSize
,
1
,
TimeUnit
.
SECONDS
,
new
ArrayBlockingQueue
<>(
16
));
new
ArrayBlockingQueue
<>(
30
));
Semaphore
semaphore
=
new
Semaphore
(
corePoolSize
);
List
<
Future
<
CapacityQueryDTO
.
Data
>>
futureDatas
=
new
ArrayList
<>();
...
...
Write
Preview
Markdown
is supported
Attach a file
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to post a comment