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 16618b85
authored
Nov 08, 2023
by
Ren Ping
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat:初始化工程师容量定时任务 dispatch
1 parent
3fd98370
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
0 additions
and
270 deletions
project-order/src/main/java/com/dituhui/pea/order/scheduler/InitEngineerCapacityScheduler.java
project-order/src/main/java/com/dituhui/pea/order/scheduler/InitEngineerCapacityScheduler.java
deleted
100644 → 0
View file @
3fd9837
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
;
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.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
com.dituhui.pea.order.service.EngineerCalendarService
;
import
com.dituhui.pea.order.service.EngineerSliceUsedCapacityService
;
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
;
import
org.springframework.scheduling.annotation.Scheduled
;
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.Collections
;
import
java.util.Comparator
;
import
java.util.List
;
import
java.util.Objects
;
import
java.util.Optional
;
import
java.util.concurrent.TimeUnit
;
import
java.util.stream.Collectors
;
@Slf4j
@Component
public
class
InitEngineerCapacityScheduler
{
@Value
(
"${scheduler.init-engineer-capacity.day-offset-begin}"
)
private
int
dayOffsetBegin
;
@Value
(
"${scheduler.init-engineer-capacity.day-offset-end}"
)
private
int
dayOffsetEnd
;
@Value
(
"${scheduler.init-engineer-capacity.rewrite-force}"
)
private
boolean
rewriteForce
;
@Autowired
private
EngineerBusinessDao
engineerBusinessDao
;
@Autowired
private
CapacityEngineerCalendarDao
capacityEngineerCalendarDao
;
@Autowired
private
CapacityEngineerStatDao
capacityEngineerStatDao
;
@Autowired
private
EngineerInfoDao
engineerInfoDao
;
@Autowired
private
TimeSliceDao
timeSliceDao
;
@Autowired
private
EngineerSliceUsedCapacityService
EngineerSliceUsedCapacityService
;
@Autowired
private
EngineerCalendarService
engineerCalendarService
;
private
boolean
verifyCalendar
(
List
<
CapacityEngineerCalendarEntity
>
configs
)
{
// 检查多条请假配置是否有交叉行为; configs已经根据startTime排序
for
(
int
i
=
0
;
i
<
configs
.
size
();
i
++)
{
for
(
int
j
=
i
+
1
;
j
<
configs
.
size
();
j
++)
{
CapacityEngineerCalendarEntity
config1
=
configs
.
get
(
i
);
CapacityEngineerCalendarEntity
config2
=
configs
.
get
(
j
);
// 必须是 config1的start<end; config2的start<end, 且config1.end <= config2.start
if
(
config1
.
getStartTime
().
isAfter
(
config1
.
getEndTime
())
||
config2
.
getStartTime
().
isAfter
(
config2
.
getStartTime
())
||
config1
.
getEndTime
().
isAfter
(
config2
.
getStartTime
()))
{
return
false
;
}
}
}
return
true
;
}
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
<
OccupyInfoDetail
>
configs
)
{
// 计算一个工程师,一天的工作容量信息
// 省略实现细节
CapacityStats
r
=
new
CapacityStats
();
EngineerBusinessEntity
businessEntity
=
engineerBusinessDao
.
getByEngineerCode
(
engineerCode
);
LocalDateTime
startTime
=
DateUtils
.
localDateTimeFromStr
(
String
.
format
(
"%s %s:00"
,
date
,
businessEntity
.
getWorkOn
()));
LocalDateTime
endTime
=
DateUtils
.
localDateTimeFromStr
(
String
.
format
(
"%s %s:00"
,
date
,
businessEntity
.
getWorkOff
()));
long
totalWorkTime
=
Duration
.
between
(
startTime
,
endTime
).
toMinutes
();
long
totalLeaveTime
=
0
;
if
(
configs
.
isEmpty
())
{
log
.
warn
(
"日期[{}]技术员[{}]无记录,当全勤处理"
,
date
,
engineerCode
);
}
else
{
totalLeaveTime
=
sumLeaveTime
(
configs
);
}
return
new
CapacityStats
().
setTotal
(
totalWorkTime
).
setUsed
(
totalLeaveTime
).
setRemain
(
totalWorkTime
-
totalLeaveTime
);
}
private
void
initOneEngineerSlice
(
String
date
,
String
engineerCode
,
List
<
OccupyInfoDetail
>
configs
,
List
<
TimeSliceEntity
>
commonTimeSliceList
)
{
//查询时间片容量
List
<
CapacityEngineerSliceUsedEntity
>
engineerTimeSlice
=
EngineerSliceUsedCapacityService
.
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
)
{
final
Long
id
=
timeSlice
.
getId
();
Optional
<
CapacityEngineerSliceUsedEntity
>
sliceUsedEntity
=
Optional
.
ofNullable
(
engineerTimeSlice
).
orElse
(
Collections
.
emptyList
())
.
stream
()
.
filter
(
t
->
Objects
.
equals
(
t
.
getTimmeSlice
().
getId
(),
id
)).
findFirst
();
CapacityEngineerSliceUsedEntity
r
=
sliceUsedEntity
.
orElseGet
(
CapacityEngineerSliceUsedEntity:
:
new
);
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
++)
{
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
);
//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
);
}
else
if
(
leaveStartTime
.
isBefore
(
sliceStartLocalTime
)
&&
leaveEndTime
.
compareTo
(
sliceEndLocalTime
)
<=
0
)
{
lengthOfLeave
+=
DateTimeUtil
.
betweenTwoTime
(
sliceStartLocalTime
,
leaveEndTime
,
TimeUnit
.
MINUTES
);
}
}
// 剩余可约容量
long
leftUseTime
=
r
.
getCapTotal
()
-
lengthOfLeave
;
r
.
setCapLeft
(
leftUseTime
);
r
.
setMaxDuration
(
leftUseTime
);
r
.
setCapUsed
(
lengthOfLeave
);
r
.
setCreateTime
(
LocalDateTime
.
now
());
r
.
setUpdateTime
(
LocalDateTime
.
now
());
resultList
.
add
(
r
);
}
EngineerSliceUsedCapacityService
.
saveAll
(
resultList
);
}
private
List
<
TimeSliceEntity
>
getTimeSliceEntities
(
LocalDateTime
workStartTime
,
LocalDateTime
workEndTime
,
List
<
TimeSliceEntity
>
commonTimeSliceList
)
{
//切片开始时间
LocalTime
sliceStartHour
=
LocalTime
.
of
(
workStartTime
.
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
->
{
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
<
OccupyInfoDetail
>
configs
=
engineerCalendarService
.
getEngineerWorkDayCalendar
(
engineerCode
,
LocalDate
.
parse
(
date
,
DateTimeUtil
.
DATE_FORMAT
));
List
<
TimeSliceEntity
>
commonTimeSliceList
=
timeSliceDao
.
findByType
(
"HOURS"
);
//初始化一个工程师时间切片容量
initOneEngineerSlice
(
date
,
engineerCode
,
configs
,
commonTimeSliceList
);
// 初始化一个工程师、一天的容量
CapacityEngineerStatEntity
statEntity
=
capacityEngineerStatDao
.
getByWorkdayAndEngineerCode
(
date
,
engineerCode
);
if
(
statEntity
!=
null
&&
!
rewriteForce
)
{
log
.
error
(
"技术员容量信息记录已存在, 直接返回"
);
return
;
}
log
.
info
(
"日期[{}] 技术员[{}] 有日历记录需要特别处理 ==="
,
date
,
engineerCode
);
CapacityStats
stats
=
calculateWorkTime
(
date
,
engineerCode
,
configs
);
log
.
info
(
"日期[{}]技术员[{}],总容量[{}] 占用容量[{}] 剩余容量[{}]"
,
date
,
engineerCode
,
stats
.
getTotal
(),
stats
.
getUsed
(),
stats
.
getRemain
());
if
(
statEntity
==
null
)
{
statEntity
=
new
CapacityEngineerStatEntity
();
statEntity
.
setEngineerCode
(
engineerCode
);
statEntity
.
setWorkday
(
date
);
statEntity
.
setCapUsedTravel
(
0
);
statEntity
.
setOrderCount
(
0
);
statEntity
.
setCreateTime
(
LocalDateTime
.
now
());
}
statEntity
.
setCapTotal
((
int
)
stats
.
getTotal
());
statEntity
.
setCapUsed
((
int
)
stats
.
getUsed
());
statEntity
.
setCapLeft
((
int
)
stats
.
getRemain
());
statEntity
.
setMaxDuration
((
int
)
stats
.
getRemain
());
statEntity
.
setUpdateTime
(
LocalDateTime
.
now
());
capacityEngineerStatDao
.
save
(
statEntity
);
log
.
info
(
"====== 处理完毕 ======"
);
}
public
void
initOneEngineerByDays
(
String
bdate
,
String
edate
,
String
engineerCode
)
{
log
.
info
(
"==== initAllEngineerByDays, bdate[{}] edate[{}]"
,
bdate
,
edate
);
LocalDate
currentDate
=
DateUtils
.
localDateFromStr
(
bdate
);
LocalDate
endDate
=
DateUtils
.
localDateFromStr
(
edate
);
List
<
String
>
allEngineerCodes
=
engineerInfoDao
.
findAll
().
stream
().
map
(
EngineerInfoEntity:
:
getEngineerCode
).
collect
(
Collectors
.
toList
());
while
(!
currentDate
.
isAfter
(
endDate
))
{
initOneEngineer
(
DateUtils
.
formatDate
(
currentDate
),
engineerCode
);
currentDate
=
currentDate
.
plusDays
(
1
);
}
}
private
void
initAllEngineerByDays
(
String
bdate
,
String
edate
)
{
log
.
info
(
"==== initAllEngineerByDays, bdate[{}] edate[{}]"
,
bdate
,
edate
);
LocalDate
currentDate
=
DateUtils
.
localDateFromStr
(
bdate
);
LocalDate
endDate
=
DateUtils
.
localDateFromStr
(
edate
);
List
<
String
>
allEngineerCodes
=
engineerInfoDao
.
findAll
().
stream
().
map
(
EngineerInfoEntity:
:
getEngineerCode
).
collect
(
Collectors
.
toList
());
while
(!
currentDate
.
isAfter
(
endDate
))
{
for
(
String
engineerCode
:
allEngineerCodes
)
{
initOneEngineer
(
DateUtils
.
formatDate
(
currentDate
),
engineerCode
);
}
currentDate
=
currentDate
.
plusDays
(
1
);
}
}
//@Scheduled(cron = "${scheduler.init-engineer-capacity.cron-expr}")
public
void
run
()
{
log
.
info
(
"开始初始化,所有工程师的容量将根据日历表的记录进行计算设置"
);
String
bdate
=
DateUtils
.
formatDate
(
LocalDate
.
now
().
plusDays
(
dayOffsetBegin
));
String
edate
=
DateUtils
.
formatDate
(
LocalDate
.
now
().
plusDays
(
dayOffsetEnd
));
initAllEngineerByDays
(
bdate
,
edate
);
}
@Data
@Accessors
(
chain
=
true
)
private
static
class
CapacityStats
{
private
long
total
;
private
long
used
;
private
long
remain
;
}
}
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